Data Mining Process

For the following analysis we will apply the CRISP-DM process as seen in the image below. First it is important to precisely define the goal and problem set. Therefore one has to understand the business needs and processes involved. Based on that, the necessary data can be acquired and prepared for the model building step.

1. CRISP-DM Process

1. CRISP-DM Process

Busines Understanding

Determine Business Objectives

First, to understand the purpose and to be able to interpret the results of the analysis, background information of the business domain is provided in the following section. Hereby, the business objectives for the analysis are defined as well as success criterias to meassure the fulfillment of the goals.

Background

Secondary Reserve Power

The German Reserve Market distinguishes between three different product types (19/24 european markets follow this pattern source):

  • Primary Reserve power (PR)
  • Secondary Reserve Power (SR)
  • Tertiary Reserve Power (TR)

Their differences concern the time aspect of power delivery. The image underneath shows the activation duration for full power and the activation time when full power is available.

2. The three-quality pattern (PR/SR/TR) in the German Reserve Market

2. The ‘three-quality’ pattern (PR/SR/TR) in the German Reserve Market

The focus of this analysis lies on the Secondary Reserve Power since its activation time is reasonable for common energy suppliers. The market for SR is also the biggest.

German Reserve Market

Along with the … law in 2009 the four german TSOs joined and control the German Reserve Market. Their duty for transparency led to the plattform regelleistung.net. There all data around the reserve market is published. Also the auction results of the SR. Those auctions are held weeklys from monday till wednesday. The offers account for one week (monday till sunday) and are divided in main period and sub period as well as negative and positive SR. An offer consists of a power price, a work price and the offered SR (negative or positive and its amount in MW).

Scenario: Facility

Objective

The overall aim is to give a more precise estimation for the call probability of working prices of the secondary reserve power. Those probabilities can be used as an input paramter for an optimization algorithm which controlls the energy purchasing and selling flow in both primary energy market and reserve energy market. By now the optimal working prices are set to exceed the contribution margin.

Success Criterias

Assess situation

Satus Quo, Resources, Requirements, Assumptions, Constraints, Terminology

Determine Data mining Goals

Goals, success criterias

Data Understanding

Next, we will focus on the understanding of the data. As mentioned previously, the main source is the transparent internet platform regelleistung.net

Collect Data

A R package exists rmarketcrawlR accessable via github which allows crawiling the platform. It also provides additional functions to approximate minutely secondary reserve calls and hence calculate marginal work prices. To get started with the package use devtools and its ìnstall_github() function.

library(devtools)
install_github("wagnertimo/rmarketcrawlR")
Example Data of 2016

Than you are able to activate the library and aquire the secondary reserve powerd data. We will focus on the year 2016 and get the results of the auctions, the 15 minute reserve calls and the 4 seconds reserve needs. The values rely to the Netzregelverbund , a composition of the four german TSOs. After all the data is crawled from the internet, the marginal work prices can be computed by approximating minutely reserve calls.

# Activate the package in the workspace
library(rmarketcrawlR)

# Set logging true to be able to comprehend the steps or errors
setLogging(TRUE)

# Get the data of 2016 for operating needs, calls and auctions of secondary reserve power from the Netzregelverbund
needs.2016 = getReserveNeeds('01.01.2016', '31.01.2016')
calls.2016 = getReserveCalls('01.01.2016', '31.01.2016', '6', 'SRL')
auctions.2016 = getReserveAuctions('01.01.2016', '31.01.2016', '2')

# Calculate the marginal work prices for 2016 with 2 cores --> This step takes a while
mwp.2016 = getMarginalWorkPrices(needs.2016, calls.2016, auctions.2016, numCores = 2)

For further information about the rmarketcrawlR package read the README.md and the documentation.

The complete Data Set of the Analysis

This analaysis will be based on the data of the years from 2011 till 2016 (2011-07-01 00:00:00 CEST till 2016-12-31 23:59:00 CET). These are 5 and a half years of data. The commands below will crawl for the observations. Pre-processing operations to prepare the raw data for analysis is described in the sections below.

# Get raw input data
needs.2016 = getReserveNeeds('01.01.2016', '31.12.2016')
calls.2016 = getReserveCalls('01.01.2016', '31.12.2016', '6', 'SRL')
auctions.2016 = getReserveAuctions('01.01.2016', '31.12.2016', '2')

needs.2015 = getReserveNeeds('01.01.2015', '31.12.2015')
calls.2015 = getReserveCalls('01.01.2015', '31.12.2015', '6', 'SRL')
auctions.2015 = getReserveAuctions('01.01.2015', '31.12.2015', '2')

needs.2014 = getReserveNeeds('01.01.2014', '31.12.2014')
calls.2014 = getReserveCalls('01.01.2014', '31.12.2014', '6', 'SRL')
auctions.2014 = getReserveAuctions('01.01.2014', '31.12.2014', '2')


needs.2013 = getReserveNeeds('01.01.2013', '31.12.2013')
calls.2013 = getReserveCalls('01.01.2013', '31.12.2013', '6', 'SRL')
auctions.2013 = getReserveAuctions('01.01.2013', '31.12.2013', '2')


needs.2012 = getReserveNeeds('01.01.2012', '31.12.2012')
calls.2012 = getReserveCalls('01.01.2012', '31.12.2012', '6', 'SRL')
auctions.2012 = getReserveAuctions('01.01.2012', '31.12.2012', '2')


needs.2011 = getReserveNeeds('01.07.2011', '31.12.2011')
calls.2011 = getReserveCalls('01.07.2011', '31.12.2011', '6', 'SRL')
auctions.2011 = getReserveAuctions('01.07.2011', '31.12.2011', '2')

Describe Data

Data Sets and Features

WHY NETZREGELVERBUND DATA???

Outliers and Missing values

Before we can use the collected raw data for pre-processing it to calculate the marginal work prices and other computations based on that, the data must be cleaned. It is neccessary to handle missing data and outliers. Both has been encountered. Outliers were identified for the operative reserve needs in 2011 and 2012 (data.frames needs.2011 and needs.2012). Those oultliers are characterized by continuous zero values over a longer period of observations. This radical change of reserve power needs is interpreted as an outlier (perhaps a meassuring error).

On the other hand there are some missing values for operative reserve needs in 2012 (data.frame needs.2012) and the operative reserve calls in 2011, 2012 and 2013 (data.frames calls.2011, calls.2012 and calls.2013). The operative needs values in 2012 show NaN values for a few specific dates. Also some reserve calls (negative and positive) of the Netzregelverbund are missing in 2011, 2012, and 2013.

All those data.frames have to be pre-processed and cleaned. The procedures are described in the next sections.

If you want to achieve the same analysis results, it is important to follow these steps since this is the data base of all calculations. It is also important to mention that without treating those outliers and missing (or NaN) values, the getMarginalWorkPrices() or getOneMinuteCalls() will not work with the compromised data sets.

Zero values for operative reserve needs

By investigating the operative reserve needs for 2011 and 2012 there have been outliers detected. There is a constant row of zero values for the reserve needs (in MW) starting at 2011-07-26 11:06:40 till 2011-07-26 12:03:32 (with an exceptional value of -1221.1385 at 2011-07-26 12:03:00). This zero row is preceeded and followed by negative reserve needs around the thousand MW area. There are also zeros in 2012 for 2012-08-07 10:19:48 till (excluding) 2012-08-07 11:33:12. The abrupt decline and long holding of those zero rows indicates outliers (both sources https://www.transnetbw.de/de/strommarkt/systemdienstleistungen/regelenergie-bedarf-und-abruf#srlbedarf2011 and http://www.50hertz.com/de/Maerkte/Regelenergie/Regelenergie-Downloadbereich show outliers).


# function to get the zero outliers
checkForZeroOutliers <- function(df) {
  # Cut the 4sec data in 15 minute blocks to count 0 values for all blocks
  df$cuttedTime <- cut(df$DateTime, breaks = paste("15", "min", sep = " "))
  df$cuttedTime <- as.POSIXct(df$cuttedTime, tz = "Europe/Berlin")
  # Create a variable to mark a zero MW value
  df$count <- ifelse(needs.2011$MW == 0, 1,0)
  
  # Count the zero markers by summing for every 15min block
  r <- df %>%
    group_by(cuttedTime) %>%
    summarise(n= sum(count)) %>%
    filter(n == 225)
  
  return(r)
}


zeroCounts <- checkForZeroOutliers(needs.2011)
head(zeroCounts)

# Output:
#            cuttedTime     n
# 1 2011-07-26 11:15:00   225
# 2 2011-07-26 11:30:00   225
# 3 2011-07-26 11:45:00   225

zeroCounts <- checkForZeroOutliers(needs.2012)
head(zeroCounts)
#            cuttedTime     n
# 1 2012-08-07 10:30:00   225
# 2 2012-08-07 10:45:00   225
# 3 2012-08-07 11:00:00   225
# 4 2012-08-07 11:15:00   225

To replace or impute those zero values we could simply take the data of the (two) year before on 2010-07-26 (2010-08-07). I will show the replacement procedure for the data of 2011. Therefore we will compare some statistics with the observations of an hour before and after the outliers.


# Get oprative reserve needs of the year before
n.2010 = getReserveNeeds('26.07.2010', '26.07.2010')

n.2011.hour.before <- needs.2011[needs.2011$DateTime >= as.POSIXct("2011-07-26 10:00:00") & needs.2011$DateTime < as.POSIXct("2011-07-26 11:00:00"),]
n.2011.hour.after <- needs.2011[needs.2011$DateTime >= as.POSIXct("2011-07-26 12:00:00") & needs.2011$DateTime < as.POSIXct("2011-07-26 13:00:00"),]
n.2010.hour.at <- n.2010[n.2010$DateTime >= as.POSIXct("2010-07-26 11:06:40") & n.2010$DateTime < as.POSIXct("2010-07-26 12:03:32"),]

summary(n.2011.hour.before$MW)
#    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
# -1578.1  -961.5  -753.8  -795.6  -600.0  -270.6 
   
summary(n.2011.hour.after$MW)
#    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
# -1597.5 -1154.6  -910.7  -881.0  -697.7     0.0 

summary(n.2010.hour.at$MW)
#     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
# -1859.19  -284.38   -72.02  -139.44    60.17   379.32  

The statistics show that the distribution is quite different. The mean and median is lower. There are also positive reserve needs in 2010. To gain a better insight of the distribution, we will plot the data with the outliers and then with the replaced 2010 data. The figure in the code snippet below shows the outliers.

# hour before and after the hour with the zero values
n.2011.hour <- needs.2011[needs.2011$DateTime >= as.POSIXct("2011-07-26 10:00:00") & needs.2011$DateTime < as.POSIXct("2011-07-26 13:00:00"),]
# Plot the outliers in context of the hour before and after
library(ggplot2)
ggplot(data = n.2011.hour, aes(x=DateTime, y = MW)) +
  geom_line() +
  labs(title="Zero Outliers For Opreative Reserve Needs on 2011-07-26")

Now it is time to fit in the 2010 data.

# Replace the zero values with the data of the same time period in 2010
n.2011.hour[n.2011.hour$DateTime >= as.POSIXct("2011-07-26 11:06:40") & n.2011.hour$DateTime < as.POSIXct("2011-07-26 12:03:32"),]$MW <- n.2010[n.2010$DateTime >= as.POSIXct("2010-07-26 11:06:40") & n.2010$DateTime < as.POSIXct("2010-07-26 12:03:32"),]$MW
# Plot the replaced data
ggplot(data = n.2011.hour, aes(x=DateTime, y = MW)) +
  geom_line() +
  labs(title="Replaced Zero Outliers With Opreative Reserve Needs of 2010")

At the begining there is a spike from around -1000 to -300 and then the 2010 data falls back to around -1800. This indicates some turbulances. One could argue and try to make this transition more smoothly. So we will modify the data a bit to smooth the extreme values and to get closer to the statistics (see in the section above) of the hours before and after.

# Replace the zero values with the data of the same time period in 2010
n10 <- n.2010[n.2010$DateTime >= as.POSIXct("2010-07-26 11:06:40") & n.2010$DateTime < as.POSIXct("2010-07-26 12:03:32"),]
n10$MW <- ifelse(n10$MW >= -1200, n10$MW - 400, n10$MW + 400)
summary(n10$MW)
    Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
-1459.19  -684.38  -472.02  -528.18  -339.83   -20.68 
n.2011.hour[n.2011.hour$DateTime >= as.POSIXct("2011-07-26 11:06:40") & n.2011.hour$DateTime < as.POSIXct("2011-07-26 12:03:32"),]$MW <- n10$MW
# Plot the modified replacing data
ggplot(data = n.2011.hour, aes(x=DateTime, y = MW)) +
  geom_line() +
  labs(title="Replaced Zero Outliers With Modified Operative Reserve Needs of 2010")

This looks quite smooth. It captures the uptrend and downtrend at around 11:30:00 and also the unique spike at the begining. The whole series lies below zero. Now we will replace those values with the original data set of the operative reserve needs in 2011 (needs.2011).

The same was done with the data of 2012. The graphic below shows the fitted hole. The data of 2010 was modified by reversing its order (such that it followed the uptrend) and adding 400 MW.

Replacing zero outliers values for 2017-08-07

Replacing zero outliers values for 2017-08-07

NaN values for operative reserve needs in 2012

Both sources https://www.transnetbw.de/de/strommarkt/systemdienstleistungen/regelenergie-bedarf-und-abruf#srlbedarf2011 and http://www.50hertz.com/de/Maerkte/Regelenergie/Regelenergie-Downloadbereich show NaN values on the dates 2012-07-04 2012-07-05 2012-07-06 and 2012-07-07.

nans <- filter(needs.2012, MW == "NaN")
unans <- unique(format(nans$DateTime, "%Y-%m-%d"))
unans
# "2012-07-04" "2012-07-05" "2012-07-06" "2012-07-07"

# Side Note:
# Time periods for the NaN values
#
# "2012-07-04 21:45:04 CEST" - "2012-07-04 21:50:36 CEST"
# "2012-07-05 21:45:08 CEST" - "2012-07-05 22:01:12 CEST"
# "2012-07-06 21:45:04 CEST" - "2012-07-06 21:52:12 CEST"
# "2012-07-07 21:45:04 CEST" - "2012-07-07 21:50:52 CEST"

Next we will look at the data with the NaN values for one (2012-07-05) of the four dates. This shows the approach how the rest will be replaced with the data of the year 2010. The data of 2011 is not taken, since this could bias the analysis as it is part of the whole data set. Optional, some values will be adjusted to fit more smoothly into the time series. Therefore we will visualize the time series data with a few values before and after the NaN values depending on the range of the NaN values.

# Get the needed data of 2010
n.2010 <- getReserveNeeds("04.07.2010","07.07.2010")
# set the date, e.g. 2012-07-05
date <- 2 
# get the time period of the NaN values
mini <- min(nans[format(nans$DateTime, "%Y-%m-%d") == as.Date(unans[date]), "DateTime"])
maxi <- max(nans[format(nans$DateTime, "%Y-%m-%d") == as.Date(unans[date]), "DateTime"])

mi <- mini - (maxi - mini) # get the date for a few minutes before
ma <- maxi + (maxi - mini) # get the date for a few minutes after


# get the data a few minutes (range of max and min) before and after the NaN values
n <- needs.2012[needs.2012$DateTime >= mi & needs.2012$DateTime <= ma,]

Now let us plot the time series data with the NaN hole which has to be filled with the data of 2010.

# plot the data with the NaN hole
ggplot(data = n, aes(x=DateTime, y = MW)) +
  geom_line() +
  labs(title = paste("NaN Values For Operative Reserve Needs on ", unans[date], sep=""))

The next step is to get the data of 2010 and replace the NaN values.

# get the dates of the last year
miny <- seq(mini, length=2, by="-2 years")[2]
maxy <- seq(maxi, length=2, by="-2 years")[2]
# get the data of the last year
nr <- n.2010[n.2010$DateTime >= miny & n.2010$DateTime <= maxy,]
# replace the NaN values with that of the last year
n[n$DateTime >= mini & n$DateTime <= maxi,]$MW <- nr$MW

Again, the plot with the replaced data gives us an insight on how well the data fits into the time series.

# plot the data with the filled hole
ggplot(data = n, aes(x=DateTime, y = MW)) +
  geom_line() +
  labs(title = paste("Replaced NaN Values For Operative Reserve Needs on ", unans[date], sep=""))

Here, the data in 2010 seems to be a bit low and the transitions at the end is a bit too steep. Hence, we will adjust those values by adding a constant value of 200 MW.

# Optional!!
# Modify and adjust the replacing values
nr$MW <- nr$MW + 200
# replace the adjusted values with that of the last year
n[n$DateTime >= mini & n$DateTime <= maxi,]$MW <- nr$MW

This time the data fits more smoothly. There is still a steep ascent of reserve needs at the begining and end, but this is acceptable.

# plot the data with the filled hole
ggplot(data = n, aes(x=DateTime, y = MW)) +
  geom_line() +
  labs(title = paste("Adjusted Replace Values For Operative Reserve Needs on ", unans[date], sep=""))

If the modification is enough, the data (nr data.frame holds the replacing values) can be replaced for the original operative needs data set of 2012.

# replace NaN VALUES OF the original data set needs.2012
needs.2012[needs.2012$DateTime >= mini & needs.2012$DateTime <= maxi,]$MW <- nr$MW

The following graphs show the respective replacement for the other three dates. There were also some adjustment made:

  • For 2012-07-04: nr$MW = nr$MW - 900
  • For 2012-07-06: nr$MW = ifelse(nr$MW < -1250, nr$MW + 800, nr$MW + 300)
  • For 2012-07-07: nr$MW = nr$MW + 1200
Replacing NaN values for 2017-07-04

Replacing NaN values for 2017-07-04

Replacing NaN values for 2017-07-06

Replacing NaN values for 2017-07-06

Replacing NaN values for 2017-07-07

Replacing NaN values for 2017-07-07

Missing Values for operative reserve calls

Missing values occur for 15 minute calls of negative and positive SR of the Netzregelverbund in 2013, 2012 and 2011:

  • 2013: There are 37 observations in 2013 with missing values. This is around 0.11%. The main date is 2013-12-04. At 15:45 till the end of the day values for negative and positive SR calls are missing. Then there are four other days (2013-04-03, 2013-06-04, 2013-09-18 and 2013-12-07) with one observation each.

  • 2012: There are 38 observations in 2012 with missing values. This is around 0.11%. The main dates are 2012-02-26 and 2012-10-27. with missing values for negative and positive SR calls in the evening and morning hours. Then there are two other days (2012-08-05, 2012-09-09) with one observation each.

  • 2011: There are 235 observations in 2011 (only half the year, starting from 2011-07-01) with missing values. This is around 1.33%. The dates are too much to list them.

# FOR 2013
nrow(calls.2013[is.na(calls.2013$neg_MW), ])
# [1] 37 --> ca. 0.11%
calls.2013[is.na(calls.2013$neg_MW), "DateTime"]
# [1] "2013-04-03 19:00:00 CEST" "2013-06-04 15:00:00 CEST"
# [3] "2013-09-18 08:30:00 CEST" "2013-12-04 15:45:00 CET" 
# [5] "2013-12-04 16:00:00 CET"  "2013-12-04 16:15:00 CET" 
# [7] "2013-12-04 16:30:00 CET"  "2013-12-04 16:45:00 CET" 
# [9] "2013-12-04 17:00:00 CET"  "2013-12-04 17:15:00 CET" 
# [11] "2013-12-04 17:30:00 CET"  "2013-12-04 17:45:00 CET" 
# [13] "2013-12-04 18:00:00 CET"  "2013-12-04 18:15:00 CET" 
# [15] "2013-12-04 18:30:00 CET"  "2013-12-04 18:45:00 CET" 
# [17] "2013-12-04 19:00:00 CET"  "2013-12-04 19:15:00 CET" 
# [19] "2013-12-04 19:30:00 CET"  "2013-12-04 19:45:00 CET" 
# [21] "2013-12-04 20:00:00 CET"  "2013-12-04 20:15:00 CET" 
# [23] "2013-12-04 20:30:00 CET"  "2013-12-04 20:45:00 CET" 
# [25] "2013-12-04 21:00:00 CET"  "2013-12-04 21:15:00 CET" 
# [27] "2013-12-04 21:30:00 CET"  "2013-12-04 21:45:00 CET" 
# [29] "2013-12-04 22:00:00 CET"  "2013-12-04 22:15:00 CET" 
# [31] "2013-12-04 22:30:00 CET"  "2013-12-04 22:45:00 CET" 
# [33] "2013-12-04 23:00:00 CET"  "2013-12-04 23:15:00 CET" 
# [35] "2013-12-04 23:30:00 CET"  "2013-12-04 23:45:00 CET" 
# [37] "2013-12-07 20:00:00 CET"

# FOR 2012
nrow(calls.2012[is.na(calls.2012$neg_MW), ])
# [1] 38 --> ca. 0.11%
calls.2012[is.na(calls.2012$neg_MW), "DateTime"]
# [1] "2012-02-26 20:30:00 CET"  "2012-02-26 20:45:00 CET" 
# [3] "2012-02-26 21:00:00 CET"  "2012-02-26 21:15:00 CET" 
# [5] "2012-02-26 21:30:00 CET"  "2012-02-26 21:45:00 CET" 
# [7] "2012-02-26 22:00:00 CET"  "2012-02-26 22:15:00 CET" 
# [9] "2012-02-26 22:30:00 CET"  "2012-02-26 22:45:00 CET" 
# [11] "2012-02-26 23:00:00 CET"  "2012-02-26 23:15:00 CET" 
# [13] "2012-02-26 23:30:00 CET"  "2012-02-26 23:45:00 CET" 
# [15] "2012-08-05 00:00:00 CEST" "2012-09-09 00:00:00 CEST"
# [17] "2012-10-27 00:00:00 CEST" "2012-10-27 00:15:00 CEST"
# [19] "2012-10-27 00:30:00 CEST" "2012-10-27 00:45:00 CEST"
# [21] "2012-10-27 01:00:00 CEST" "2012-10-27 01:15:00 CEST"
# [23] "2012-10-27 01:30:00 CEST" "2012-10-27 01:45:00 CEST"
# [25] "2012-10-27 02:00:00 CEST" "2012-10-27 02:15:00 CEST"
# [27] "2012-10-27 02:30:00 CEST" "2012-10-27 02:45:00 CEST"
# [29] "2012-10-27 03:00:00 CEST" "2012-10-27 03:15:00 CEST"
# [31] "2012-10-27 03:30:00 CEST" "2012-10-27 03:45:00 CEST"
# [33] "2012-10-27 04:00:00 CEST" "2012-10-27 04:15:00 CEST"
# [35] "2012-10-27 04:30:00 CEST" "2012-10-27 04:45:00 CEST"
# [37] "2012-10-27 05:00:00 CEST" "2012-10-27 05:15:00 CEST"

# FOR 2011
nrow(calls.2011[is.na(calls.2011$neg_MW), ])
# [1] 235 --> ca. 1.33%
calls.2011[is.na(calls.2011$neg_MW), "DateTime"]
# (..)

Impute Missing Values

For time series analysis on such a data set, it is not appropriate to leave them out. The amount of proportion is quite less, but nevertheless it is better to approximate the missing values. There would be three different approaches:

  • Use the data of other years: Since there is no intersection with the missing dates, it is possible to impute the missing values with the values of the year before. Only problem is that for 2011 (also for february 2012) no data of 2010 (february 2011) is available. Another option would be to take an average of the future data of the years after.
  • Use reserve needs: It is also possible to use the 4sec reserve needs which are also used for the minutely approximation of the calls.
  • Use the data of the TSOs: On the given date of missing values, some TSO show SR call data. So one approach would be to sum up those observations and use it to impute the missing values for the Netzregelverbund.

We will go for the latter. Taking the reserve needs would cause a big deviation, since the needs and actual calls differ remarkably. Averaging future years would lead to misinterpretations. Using TSO data seems natural because the summation of the four yield into the Netzregelverbund variable. It is also noticed that only one (only for 2011 at 2011-10-30 23:00:00 for one hour 3 TSO lack with data) TSO has missing values for the dates of interest which should keep the error and a possible bias small. This leaves the question how to impute the missing value of that TSO. A simple approach would be to take the value of the day before. This is possible because the first observation has no missing value(2011-07-01). The code sample below imputes the data for the already downloaded data.frames calls.2013 calls.2012 and calls.2011.

# Use the call data of the 4 TSOs to impute the missing values of the calls (Netzregelverbund) in 2013, 2012 and 2011

# Load the TSO data
# 50Hz (4)
calls.2013.4 = getReserveCalls('01.01.2013', '31.12.2013', '4', 'SRL')
calls.2012.4 = getReserveCalls('01.01.2012', '31.12.2012', '4', 'SRL')
calls.2011.4 = getReserveCalls('01.07.2011', '31.12.2011', '4', 'SRL')
# TenneT (2)
calls.2013.2 = getReserveCalls('01.01.2013', '31.12.2013', '2', 'SRL')
calls.2012.2 = getReserveCalls('01.01.2012', '31.12.2012', '2', 'SRL')
calls.2011.2 = getReserveCalls('01.07.2011', '31.12.2011', '2', 'SRL')
# Amprion (3)
calls.2013.3 = getReserveCalls('01.01.2013', '31.12.2013', '3', 'SRL')
calls.2012.3 = getReserveCalls('01.01.2012', '31.12.2012', '3', 'SRL')
calls.2011.3 = getReserveCalls('01.07.2011', '31.12.2011', '3', 'SRL')
# TransnetBW (1)
calls.2013.1 = getReserveCalls('01.01.2013', '31.12.2013', '1', 'SRL')
calls.2012.1 = getReserveCalls('01.01.2012', '31.12.2012', '1', 'SRL')
calls.2011.1 = getReserveCalls('01.07.2011', '31.12.2011', '1', 'SRL')

tso.list.2013 <- list(calls.2013.1,calls.2013.2,calls.2013.3,calls.2013.4)
tso.list.2012 <- list(calls.2012.1,calls.2012.2,calls.2012.3,calls.2012.4)
tso.list.2011 <- list(calls.2011.1,calls.2011.2,calls.2011.3,calls.2011.4)

#
# Impute missing calls with TSO data
#
imputed.calls.2013 <- imputeMissingCallsWithTSO(calls.2013, tso.list.2013)
imputed.calls.2012 <- imputeMissingCallsWithTSO(calls.2012, tso.list.2012)
imputed.calls.2011 <- imputeMissingCallsWithTSO(calls.2011, tso.list.2011)

# Sanity check that no NA are left
imputed.calls.2013[is.na(imputed.calls.2013$neg_MW), ]
imputed.calls.2012[is.na(imputed.calls.2012$neg_MW), ]
imputed.calls.2011[is.na(imputed.calls.2011$neg_MW), ]

Further references in the analysis to calls.2013 calls.2012 and calls.2011 contain the imputed data sets.

Approximation of minutely calls

Since we need a higher resolution for the operative reserve calls (one minute instead of 15 minutes), the next step is now to approximate one minute operative reserve calls from the operative reserve needs data.

The approximation follows several steps:

  1. The 4sec operative reserve needs are averaged on an one minute base. Those 15 observations build the base value which is going to be corrected by the difference of its 15min average and the reserve call value.

  2. The 15min reserve need averages are computed for positive and negative needs and compared to the positive and negative reserve calls respectively.

  3. The difference value of the positive (negative) average gets evenly distributed among the positive (negative) minutely reserve needs within that 15 minutes.

  4. This corrected average difference (fractioned by the number of positive (negative) reserve needs) is added up to the one minute reserve need value.

After this procedure the 15 miunte averages of the corrected one minute reserve need should equal the retrieved reserve calls (for positive and negative calls respectively). The figure below shows visually the approximation. The light grey solid line represents the 4sec reserve needs on the 2016-01-01 00:15 - 00:30. The solid blue step line is the 1min average and the solid green (red) line is the 15min average of positive (negative) reserve needs. On the other hand, you can see the dashed green (red) line which represents the 15min reserve calls. Since the time period is only 15 minutes we see a straight line. The approximated 1min call is the dashed blue step line. You can notice the downward correction for the negative reserves, due to the negative deviation of the 15min average and the reserve call (solid vs. dashed red line). Vice versa, the positive needs are corrected upwards. The corrections are all of the same amount for each minute (depending on positive or negative number of minutes) and are slightly higher than the delta between the 15min average of the reserve needs and the calls, because less positive (negative) minutes have to compensate the whole 15 minute value.

Approximation of 1min calls (2016-01-01 00:15 - 00:30)

Approximation of 1min calls (2016-01-01 00:15 - 00:30)

Special cases

So far you have seen a normal case. But the deviations between reserve needs and calls can be divergent such that the 15min average for the needs shows no value where as reserve calls had been activated. This special case is called homogenity. There can be two types of homogenity:

  • Negative homogenity occurs if all 1min averaged needs within the 15min period are negative (i.e. 15min average is zero), but the 15 minute reserve call show demand for positive reserve power.

  • Positive homogenity occurs if all 1min averaged needs within the 15min period are positive (i.e. 15min average is zero), but the 15 minute reserve call show demand for negative reserve power.

Those two cases are illustrated in the following two graphs. The first graph shows negative homogenity. Hereby, all blue solid lines are in the negative power, but the dashed green light is not aligned with the solid green line on the value of 0 MW. This means there was demand for positive reserve power. The second plot deals with the opposite, negative homogenity case.

Approximation of 1min calls with special case of negative homogenity (2016-01-01 00:00 - 00:15)

Approximation of 1min calls with special case of negative homogenity (2016-01-01 00:00 - 00:15)

Approximation of 1min calls with special case of positive homogenity (2016-01-01 00:30 - 00:45)

Approximation of 1min calls with special case of positive homogenity (2016-01-01 00:30 - 00:45)

To encounter those special cases. We have to add a minute of positive (in the negative homogentiy case) or negative (in the positive homogenity case) reserve power which compensates the whole 15 minute reserve call power. The chosen minute will be the smallest absolute value of the positive (negative) powers. Hence, the 15 minute averages have to be newly computed. And then the new difference yields in the new correction value.

The next plot shows the result of the correction of the negative homogenity case example. You can see that the minute of the 1min averaged reserve need with the lowest negative power value is selected as the positive reserve power minute to compensate the whole 15 minutes. Similarly, the 15 minute average for the negative minutes is updated and the new delta is added up for the correction of those minutes. Now the dashed and solid lines are aligned.

Final result of the approximation of 1min calls with special case of negative homogenity (2016-01-01 00:00 - 00:15)

Final result of the approximation of 1min calls with special case of negative homogenity (2016-01-01 00:00 - 00:15)

Now it could happen that with the new correction another (or more) approximated 1 minute call(s) switches its sign. This case is called zero crossing. All in all this forces to an iterative approximation process. Thereby, the values get corrected as long as the new 15min average equals the reserve call. The graphic below illustrates such a case on the 2016-01-01 12:15. The approximation, as it would occur in the normal case, yields to change signs for four minutes which showed negative reserve power needs before. At this state the positive 15 minute average (accounting the four minute values) would not match with the reserve call value. Hence, another iteration (correction process) is needed to equalize both values.

First iteration of the approximation of 1min calls with zero crossing values (2016-01-01 12:15 - 12:30)

First iteration of the approximation of 1min calls with zero crossing values (2016-01-01 12:15 - 12:30)

In the plot below you can see the result after another correction. Now, the 15 minute average of the reserve needs equals the reserve calls. The values of the four positive minutes were lowered, where as the negative minutes rose since fewer values have to compensate the positive delta between the reserve calls and needs.

Final iteration of the approximation of 1min calls with zero crossing values (2016-01-01 12:15 - 12:30)

Final iteration of the approximation of 1min calls with zero crossing values (2016-01-01 12:15 - 12:30)

The rmarketcrawlR package includes a function to calculate the approximation. The code snippet below shows an example on how to get the approximations for 2016. If you are interested in the marginal work prices (as we are), you can skip this function, since the method for computing marginal work prices already contains this correction procedure.

# Calculate the approximated 1min calls from the 4sec operating reserve needs data
approx.calls.2016 = getOneMinuteCalls(needs.2016, calls.2016)
Calculation of marginal work prices and call probabilities

In the chapters before we dived into the data and pre-processed it. It was also shown how the approximation of the one minute calls is calculated. Now it is time to use the pre-processed data to calculate the marginal work prices for every approximated minute. The output of the approximation function getOneMinuteCalls() is not needed, since the getMarginalWorkPrices() also computes the approximation.

The calculation is straight forward. For every minute (observation) the approximated reserve power (positive or negaative), the related time (and its tarif indication) is matched up with the auctions data. The match up orders the auctions from lowest to highest work price bid. Its offered power is cumulated. The bid (offer) where its cumulated value equals or exceeds the approximated reserve power defines the marginal work price. Offers equal or below this price had been called to deliver the reserve power.

To proceed with the analysis, we will now use the getMarginalWorkPrices() of the R package to calculate the marginal work prices for 2011 till 2016 on a minutely resolution.

# Calculate the marginal work prices from the preprocessed raw input data (see sections above) for the years 2011 - 2016
mwp.2016 = getMarginalWorkPrices(needs.2016, calls.2016, auctions.2016, numCores = 2)
mwp.2015 = getMarginalWorkPrices(needs.2015, calls.2015, auctions.2015, numCores = 2)
mwp.2014 = getMarginalWorkPrices(needs.2014, calls.2014, auctions.2014, numCores = 2)
mwp.2013 = getMarginalWorkPrices(needs.2013, calls.2013, auctions.2013, numCores = 2)
mwp.2012 = getMarginalWorkPrices(needs.2012, calls.2012, auctions.2012, numCores = 2)
mwp.2011 = getMarginalWorkPrices(needs.2011, calls.2011, auctions.2011, numCores = 2)

# Combine all years into one data set
mwp.2011.2016 = rbind(mwp.2011, mwp.20112, mwp.2013, mwp.2014, mwp.2015, mwp.2016)

Now, we have combined all the years of data to a single data.frame. Let us have look at the first observations and the structure of the data.

str(mwp.2011.2016)
'data.frame':   2895900 obs. of  11 variables:
 $ DateTime           : Factor w/ 2895540 levels "2011-07-01 00:00:00",..: 1 2 3 4 5 6 7 8 9 10 ...
 $ approx_1min_call   : num  -507 677 506 578 216 ...
 $ neg_MW             : num  -176 -176 -176 -176 -176 ...
 $ pos_MW             : num  233 233 233 233 233 ...
 $ num_recursions     : int  1 1 1 1 1 1 1 1 1 1 ...
 $ Tarif              : Factor w/ 2 levels "HT","NT": 2 2 2 2 2 2 2 2 2 2 ...
 $ Direction          : Factor w/ 2 levels "NEG","POS": 1 2 2 2 2 2 2 2 2 2 ...
 $ marginal_work_price: num  1.45 100 98.4 99.98 83 ...
 $ num_orders         : int  22 36 29 34 15 15 17 14 39 6 ...
 $ total_num_orders   : int  58 68 68 68 68 68 68 68 68 68 ...
 $ fill_quote         : num  0.379 0.529 0.426 0.5 0.221 ...

Explore Data

Auctions Data

Firstly, we will investigate the auction results for the SR in 2016. Since we are interested in the work prices, we are going to plot the average work price bid for each week seperated by main and sub period as well as negative and positive SR.

# filter by direction
# and get the average of the work price offers
auctions.2016 %>%
  group_by_(.dots = c("date_from", "Direction")) %>%
  summarise(avg = mean(work_price)) %>%
  ggplot(aes(x = date_from, y = avg, colour = Direction)) + 
        geom_line()
Marginal Work Prices
ggplot(data = data.pos, aes(x=marginal_work_price)) +
  geom_histogram(aes(y = (..count..)/sum(..count..)), breaks = seq(0, 100, 1),
                 col="green",
                 fill="green",
                 alpha = .5) +
  labs(title="Histogram for Marginal Work Prices with positive Reserve Power in 2016") +
  labs(x="Marginal Work Price", y="Count")
LS0tCnRpdGxlOiAiQW5hbHl6aW5nIHRoZSBHZXJtYW4gT3BlcmF0aW5nIFJlc2VydmUgTWFya2V0IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZmlnX3dpZHRoOiA2IApmaWdfaGVpZ2h0OiA0Ci0tLQoKCgoKIyMgRGF0YSBNaW5pbmcgUHJvY2VzcwoKRm9yIHRoZSBmb2xsb3dpbmcgYW5hbHlzaXMgd2Ugd2lsbCBhcHBseSB0aGUgQ1JJU1AtRE0gcHJvY2VzcyBhcyBzZWVuIGluIHRoZSBpbWFnZSBiZWxvdy4gRmlyc3QgaXQgaXMgaW1wb3J0YW50IHRvIHByZWNpc2VseSBkZWZpbmUgdGhlIGdvYWwgYW5kIHByb2JsZW0gc2V0LiBUaGVyZWZvcmUgb25lIGhhcyB0byB1bmRlcnN0YW5kIHRoZSBidXNpbmVzcyBuZWVkcyBhbmQgcHJvY2Vzc2VzIGludm9sdmVkLiBCYXNlZCBvbiB0aGF0LCB0aGUgbmVjZXNzYXJ5IGRhdGEgY2FuIGJlIGFjcXVpcmVkIGFuZCBwcmVwYXJlZCBmb3IgdGhlIG1vZGVsIGJ1aWxkaW5nIHN0ZXAuCgoKIVsxLiBDUklTUC1ETSBQcm9jZXNzXSgvVXNlcnMvVGltby9Eb2N1bWVudHMvU3R1ZGl1bS8yLlwgTWFzdGVyL01hc3RlcmFyYmVpdC9cWzNcXV9BdXNhcmJlaXR1bmcvTGFUZXgvR0VSX3ZlcnNpb24vMV9ncmFwaGljcy9jcmlzcF9kbS5wbmcpeyB3aWR0aD00MiUgfQoKCiMjIEJ1c2luZXMgVW5kZXJzdGFuZGluZwoKIyMjIERldGVybWluZSBCdXNpbmVzcyBPYmplY3RpdmVzCgpGaXJzdCwgdG8gdW5kZXJzdGFuZCB0aGUgcHVycG9zZSBhbmQgdG8gYmUgYWJsZSB0byBpbnRlcnByZXQgdGhlIHJlc3VsdHMgb2YgdGhlIGFuYWx5c2lzLCBiYWNrZ3JvdW5kIGluZm9ybWF0aW9uIG9mIHRoZSBidXNpbmVzcyBkb21haW4gaXMgcHJvdmlkZWQgaW4gdGhlIGZvbGxvd2luZyBzZWN0aW9uLiBIZXJlYnksIHRoZSBidXNpbmVzcyBvYmplY3RpdmVzIGZvciB0aGUgYW5hbHlzaXMgYXJlIGRlZmluZWQgYXMgd2VsbCBhcyBzdWNjZXNzIGNyaXRlcmlhcyB0byBtZWFzc3VyZSB0aGUgZnVsZmlsbG1lbnQgb2YgdGhlIGdvYWxzLgoKIyMjIyBCYWNrZ3JvdW5kCgoKIyMjIyMgU2Vjb25kYXJ5IFJlc2VydmUgUG93ZXIKClRoZSBHZXJtYW4gUmVzZXJ2ZSBNYXJrZXQgZGlzdGluZ3Vpc2hlcyBiZXR3ZWVuIHRocmVlIGRpZmZlcmVudCBwcm9kdWN0IHR5cGVzICgxOS8yNCBldXJvcGVhbiBtYXJrZXRzIGZvbGxvdyB0aGlzIHBhdHRlcm4gW3NvdXJjZV0oaHR0cDovL3d3dy5zdHJvbW1hcmt0dHJlZmZlbi5vcmcvMjAxNi0xLTIyLU9ja2VyLUdlcm1hbi1TZWNvbmRhcnktQmFsYW5jaW5nLVBvd2VyLU1hcmtldC5wZGYucGRmKSk6CgotICoqUHJpbWFyeSBSZXNlcnZlIHBvd2VyIChQUikqKgotICoqU2Vjb25kYXJ5IFJlc2VydmUgUG93ZXIgKFNSKSoqCi0gKipUZXJ0aWFyeSBSZXNlcnZlIFBvd2VyIChUUikqKgoKVGhlaXIgZGlmZmVyZW5jZXMgY29uY2VybiB0aGUgdGltZSBhc3BlY3Qgb2YgcG93ZXIgZGVsaXZlcnkuIFRoZSBpbWFnZSB1bmRlcm5lYXRoIHNob3dzIHRoZSBhY3RpdmF0aW9uIGR1cmF0aW9uIGZvciBmdWxsIHBvd2VyIGFuZCB0aGUgYWN0aXZhdGlvbiB0aW1lIHdoZW4gZnVsbCBwb3dlciBpcyBhdmFpbGFibGUuIAoKCiFbMi4gVGhlICd0aHJlZS1xdWFsaXR5JyBwYXR0ZXJuIChQUi9TUi9UUikgaW4gdGhlIEdlcm1hbiBSZXNlcnZlIE1hcmtldF0oL1VzZXJzL1RpbW8vRG9jdW1lbnRzL1N0dWRpdW0vMi5cIE1hc3Rlci9NYXN0ZXJhcmJlaXQvXFszXF1fQXVzYXJiZWl0dW5nL0xhVGV4L0dFUl92ZXJzaW9uLzFfZ3JhcGhpY3MvcmxfdGltZWxpbmUucG5nKXsgd2lkdGg9NTAlIH0KClRoZSBmb2N1cyBvZiB0aGlzIGFuYWx5c2lzIGxpZXMgb24gdGhlIFNlY29uZGFyeSBSZXNlcnZlIFBvd2VyIHNpbmNlIGl0cyBhY3RpdmF0aW9uIHRpbWUgaXMgcmVhc29uYWJsZSBmb3IgY29tbW9uIGVuZXJneSBzdXBwbGllcnMuIFRoZSBtYXJrZXQgZm9yIFNSIGlzIGFsc28gdGhlIGJpZ2dlc3QuIAoKCgoKIyMjIyMgR2VybWFuIFJlc2VydmUgTWFya2V0CgpBbG9uZyB3aXRoIHRoZSAuLi4gbGF3IGluIDIwMDkgdGhlIGZvdXIgZ2VybWFuIFRTT3Mgam9pbmVkIGFuZCBjb250cm9sIHRoZSBHZXJtYW4gUmVzZXJ2ZSBNYXJrZXQuIFRoZWlyIGR1dHkgZm9yIHRyYW5zcGFyZW5jeSBsZWQgdG8gdGhlIHBsYXR0Zm9ybSBbcmVnZWxsZWlzdHVuZy5uZXRdKGh0dHBzOi8vd3d3LnJlZ2VsbGVpc3R1bmcubmV0KS4gVGhlcmUgYWxsIGRhdGEgYXJvdW5kIHRoZSByZXNlcnZlIG1hcmtldCBpcyBwdWJsaXNoZWQuIEFsc28gdGhlIGF1Y3Rpb24gcmVzdWx0cyBvZiB0aGUgU1IuIFRob3NlIGF1Y3Rpb25zIGFyZSBoZWxkIHdlZWtseXMgZnJvbSBtb25kYXkgdGlsbCB3ZWRuZXNkYXkuIFRoZSBvZmZlcnMgYWNjb3VudCBmb3Igb25lIHdlZWsgKG1vbmRheSB0aWxsIHN1bmRheSkgYW5kIGFyZSBkaXZpZGVkIGluIG1haW4gcGVyaW9kIGFuZCBzdWIgcGVyaW9kIGFzIHdlbGwgYXMgbmVnYXRpdmUgYW5kIHBvc2l0aXZlIFNSLiBBbiBvZmZlciBjb25zaXN0cyBvZiBhIHBvd2VyIHByaWNlLCBhIHdvcmsgcHJpY2UgYW5kIHRoZSBvZmZlcmVkIFNSIChuZWdhdGl2ZSBvciBwb3NpdGl2ZSBhbmQgaXRzIGFtb3VudCBpbiBNVykuIAoKCgoKIyMjIyMgU2NlbmFyaW86IEZhY2lsaXR5IAoKCgojIyMjIE9iamVjdGl2ZQoKVGhlIG92ZXJhbGwgYWltIGlzIHRvIGdpdmUgYSBtb3JlIHByZWNpc2UgZXN0aW1hdGlvbiBmb3IgdGhlIGNhbGwgcHJvYmFiaWxpdHkgb2Ygd29ya2luZyBwcmljZXMgb2YgdGhlIHNlY29uZGFyeSByZXNlcnZlIHBvd2VyLiBUaG9zZSBwcm9iYWJpbGl0aWVzIGNhbiBiZSB1c2VkIGFzIGFuIGlucHV0IHBhcmFtdGVyIGZvciBhbiBvcHRpbWl6YXRpb24gYWxnb3JpdGhtIHdoaWNoIGNvbnRyb2xscyB0aGUgZW5lcmd5IHB1cmNoYXNpbmcgYW5kIHNlbGxpbmcgZmxvdyBpbiBib3RoIHByaW1hcnkgZW5lcmd5IG1hcmtldCBhbmQgcmVzZXJ2ZSBlbmVyZ3kgbWFya2V0LiBCeSBub3cgdGhlIG9wdGltYWwgd29ya2luZyBwcmljZXMgYXJlIHNldCB0byBleGNlZWQgdGhlIGNvbnRyaWJ1dGlvbiBtYXJnaW4uCgoKIyMjIyBTdWNjZXNzIENyaXRlcmlhcwoKCgoKIyMjIEFzc2VzcyBzaXR1YXRpb24KClNhdHVzIFF1bywgUmVzb3VyY2VzLCBSZXF1aXJlbWVudHMsIEFzc3VtcHRpb25zLCBDb25zdHJhaW50cywgVGVybWlub2xvZ3kKCgoKCgoKCiMjIyBEZXRlcm1pbmUgRGF0YSBtaW5pbmcgR29hbHMKCkdvYWxzLCBzdWNjZXNzIGNyaXRlcmlhcwoKCgoKCgoKCgojIyMgRGF0YSBVbmRlcnN0YW5kaW5nCgpOZXh0LCB3ZSB3aWxsIGZvY3VzIG9uIHRoZSB1bmRlcnN0YW5kaW5nIG9mIHRoZSBkYXRhLiBBcyBtZW50aW9uZWQgcHJldmlvdXNseSwgdGhlIG1haW4gc291cmNlIGlzIHRoZSB0cmFuc3BhcmVudCBpbnRlcm5ldCBwbGF0Zm9ybSBbcmVnZWxsZWlzdHVuZy5uZXRdKGh0dHBzOi8vd3d3LnJlZ2VsbGVpc3R1bmcubmV0KQoKIyMjIyBDb2xsZWN0IERhdGEKCkEgUiBwYWNrYWdlIGV4aXN0cyBgcm1hcmtldGNyYXdsUmAgYWNjZXNzYWJsZSB2aWEgW2dpdGh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL3dhZ25lcnRpbW8vcm1hcmtldGNyYXdsUikgd2hpY2ggYWxsb3dzIGNyYXdpbGluZyB0aGUgcGxhdGZvcm0uIEl0IGFsc28gcHJvdmlkZXMgYWRkaXRpb25hbCBmdW5jdGlvbnMgdG8gYXBwcm94aW1hdGUgbWludXRlbHkgc2Vjb25kYXJ5IHJlc2VydmUgY2FsbHMgYW5kIGhlbmNlIGNhbGN1bGF0ZSBtYXJnaW5hbCB3b3JrIHByaWNlcy4gClRvIGdldCBzdGFydGVkIHdpdGggdGhlIHBhY2thZ2UgdXNlIGBkZXZ0b29sc2AgYW5kIGl0cyBgw6xuc3RhbGxfZ2l0aHViKClgIGZ1bmN0aW9uLgoKYGBge3J9CmxpYnJhcnkoZGV2dG9vbHMpCmluc3RhbGxfZ2l0aHViKCJ3YWduZXJ0aW1vL3JtYXJrZXRjcmF3bFIiKQpgYGAKCiMjIyMjIEV4YW1wbGUgRGF0YSBvZiAyMDE2CgpUaGFuIHlvdSBhcmUgYWJsZSB0byBhY3RpdmF0ZSB0aGUgbGlicmFyeSBhbmQgYXF1aXJlIHRoZSBzZWNvbmRhcnkgcmVzZXJ2ZSBwb3dlcmQgZGF0YS4gV2Ugd2lsbCBmb2N1cyBvbiB0aGUgeWVhciAyMDE2IGFuZCBnZXQgdGhlIHJlc3VsdHMgb2YgdGhlIGF1Y3Rpb25zLCB0aGUgMTUgbWludXRlIHJlc2VydmUgY2FsbHMgYW5kIHRoZSA0IHNlY29uZHMgcmVzZXJ2ZSBuZWVkcy4gVGhlIHZhbHVlcyByZWx5IHRvIHRoZSAqTmV0enJlZ2VsdmVyYnVuZCogLCBhIGNvbXBvc2l0aW9uIG9mIHRoZSBmb3VyIGdlcm1hbiBUU09zLiBBZnRlciBhbGwgdGhlIGRhdGEgaXMgY3Jhd2xlZCBmcm9tIHRoZSBpbnRlcm5ldCwgdGhlIG1hcmdpbmFsIHdvcmsgcHJpY2VzIGNhbiBiZSBjb21wdXRlZCBieSBhcHByb3hpbWF0aW5nIG1pbnV0ZWx5IHJlc2VydmUgY2FsbHMuCgpgYGB7cn0KIyBBY3RpdmF0ZSB0aGUgcGFja2FnZSBpbiB0aGUgd29ya3NwYWNlCmxpYnJhcnkocm1hcmtldGNyYXdsUikKCiMgU2V0IGxvZ2dpbmcgdHJ1ZSB0byBiZSBhYmxlIHRvIGNvbXByZWhlbmQgdGhlIHN0ZXBzIG9yIGVycm9ycwpzZXRMb2dnaW5nKFRSVUUpCgojIEdldCB0aGUgZGF0YSBvZiAyMDE2IGZvciBvcGVyYXRpbmcgbmVlZHMsIGNhbGxzIGFuZCBhdWN0aW9ucyBvZiBzZWNvbmRhcnkgcmVzZXJ2ZSBwb3dlciBmcm9tIHRoZSBOZXR6cmVnZWx2ZXJidW5kCm5lZWRzLjIwMTYgPSBnZXRSZXNlcnZlTmVlZHMoJzAxLjAxLjIwMTYnLCAnMzEuMDEuMjAxNicpCmNhbGxzLjIwMTYgPSBnZXRSZXNlcnZlQ2FsbHMoJzAxLjAxLjIwMTYnLCAnMzEuMDEuMjAxNicsICc2JywgJ1NSTCcpCmF1Y3Rpb25zLjIwMTYgPSBnZXRSZXNlcnZlQXVjdGlvbnMoJzAxLjAxLjIwMTYnLCAnMzEuMDEuMjAxNicsICcyJykKCiMgQ2FsY3VsYXRlIHRoZSBtYXJnaW5hbCB3b3JrIHByaWNlcyBmb3IgMjAxNiB3aXRoIDIgY29yZXMgLS0+IFRoaXMgc3RlcCB0YWtlcyBhIHdoaWxlCm13cC4yMDE2ID0gZ2V0TWFyZ2luYWxXb3JrUHJpY2VzKG5lZWRzLjIwMTYsIGNhbGxzLjIwMTYsIGF1Y3Rpb25zLjIwMTYsIG51bUNvcmVzID0gMikKYGBgCgoKPiBGb3IgZnVydGhlciBpbmZvcm1hdGlvbiBhYm91dCB0aGUgIGBybWFya2V0Y3Jhd2xSYCBwYWNrYWdlIHJlYWQgdGhlIGBSRUFETUUubWRgIGFuZCB0aGUgZG9jdW1lbnRhdGlvbi4KCiMjIyMjIFRoZSBjb21wbGV0ZSBEYXRhIFNldCBvZiB0aGUgQW5hbHlzaXMKClRoaXMgYW5hbGF5c2lzIHdpbGwgYmUgYmFzZWQgb24gdGhlIGRhdGEgb2YgdGhlIHllYXJzIGZyb20gMjAxMSB0aWxsIDIwMTYgKGAyMDExLTA3LTAxIDAwOjAwOjAwIENFU1RgIHRpbGwgYDIwMTYtMTItMzEgMjM6NTk6MDAgQ0VUYCkuIFRoZXNlIGFyZSA1IGFuZCBhIGhhbGYgeWVhcnMgb2YgZGF0YS4gVGhlIGNvbW1hbmRzIGJlbG93IHdpbGwgY3Jhd2wgZm9yIHRoZSBvYnNlcnZhdGlvbnMuIFByZS1wcm9jZXNzaW5nIG9wZXJhdGlvbnMgdG8gcHJlcGFyZSB0aGUgcmF3IGRhdGEgZm9yIGFuYWx5c2lzIGlzIGRlc2NyaWJlZCBpbiB0aGUgc2VjdGlvbnMgYmVsb3cuCgpgYGB7cn0KIyBHZXQgcmF3IGlucHV0IGRhdGEKbmVlZHMuMjAxNiA9IGdldFJlc2VydmVOZWVkcygnMDEuMDEuMjAxNicsICczMS4xMi4yMDE2JykKY2FsbHMuMjAxNiA9IGdldFJlc2VydmVDYWxscygnMDEuMDEuMjAxNicsICczMS4xMi4yMDE2JywgJzYnLCAnU1JMJykKYXVjdGlvbnMuMjAxNiA9IGdldFJlc2VydmVBdWN0aW9ucygnMDEuMDEuMjAxNicsICczMS4xMi4yMDE2JywgJzInKQoKbmVlZHMuMjAxNSA9IGdldFJlc2VydmVOZWVkcygnMDEuMDEuMjAxNScsICczMS4xMi4yMDE1JykKY2FsbHMuMjAxNSA9IGdldFJlc2VydmVDYWxscygnMDEuMDEuMjAxNScsICczMS4xMi4yMDE1JywgJzYnLCAnU1JMJykKYXVjdGlvbnMuMjAxNSA9IGdldFJlc2VydmVBdWN0aW9ucygnMDEuMDEuMjAxNScsICczMS4xMi4yMDE1JywgJzInKQoKbmVlZHMuMjAxNCA9IGdldFJlc2VydmVOZWVkcygnMDEuMDEuMjAxNCcsICczMS4xMi4yMDE0JykKY2FsbHMuMjAxNCA9IGdldFJlc2VydmVDYWxscygnMDEuMDEuMjAxNCcsICczMS4xMi4yMDE0JywgJzYnLCAnU1JMJykKYXVjdGlvbnMuMjAxNCA9IGdldFJlc2VydmVBdWN0aW9ucygnMDEuMDEuMjAxNCcsICczMS4xMi4yMDE0JywgJzInKQoKCm5lZWRzLjIwMTMgPSBnZXRSZXNlcnZlTmVlZHMoJzAxLjAxLjIwMTMnLCAnMzEuMTIuMjAxMycpCmNhbGxzLjIwMTMgPSBnZXRSZXNlcnZlQ2FsbHMoJzAxLjAxLjIwMTMnLCAnMzEuMTIuMjAxMycsICc2JywgJ1NSTCcpCmF1Y3Rpb25zLjIwMTMgPSBnZXRSZXNlcnZlQXVjdGlvbnMoJzAxLjAxLjIwMTMnLCAnMzEuMTIuMjAxMycsICcyJykKCgpuZWVkcy4yMDEyID0gZ2V0UmVzZXJ2ZU5lZWRzKCcwMS4wMS4yMDEyJywgJzMxLjEyLjIwMTInKQpjYWxscy4yMDEyID0gZ2V0UmVzZXJ2ZUNhbGxzKCcwMS4wMS4yMDEyJywgJzMxLjEyLjIwMTInLCAnNicsICdTUkwnKQphdWN0aW9ucy4yMDEyID0gZ2V0UmVzZXJ2ZUF1Y3Rpb25zKCcwMS4wMS4yMDEyJywgJzMxLjEyLjIwMTInLCAnMicpCgoKbmVlZHMuMjAxMSA9IGdldFJlc2VydmVOZWVkcygnMDEuMDcuMjAxMScsICczMS4xMi4yMDExJykKY2FsbHMuMjAxMSA9IGdldFJlc2VydmVDYWxscygnMDEuMDcuMjAxMScsICczMS4xMi4yMDExJywgJzYnLCAnU1JMJykKYXVjdGlvbnMuMjAxMSA9IGdldFJlc2VydmVBdWN0aW9ucygnMDEuMDcuMjAxMScsICczMS4xMi4yMDExJywgJzInKQoKYGBgCgoKCiMjIyMgRGVzY3JpYmUgRGF0YQoKIyMjIyMgRGF0YSBTZXRzIGFuZCBGZWF0dXJlcwoKCioqV0hZIE5FVFpSRUdFTFZFUkJVTkQgREFUQT8/PyoqCgojIyMjIyBPdXRsaWVycyBhbmQgTWlzc2luZyB2YWx1ZXMKCkJlZm9yZSB3ZSBjYW4gdXNlIHRoZSBjb2xsZWN0ZWQgcmF3IGRhdGEgZm9yIHByZS1wcm9jZXNzaW5nIGl0IHRvIGNhbGN1bGF0ZSB0aGUgbWFyZ2luYWwgd29yayBwcmljZXMgYW5kIG90aGVyIGNvbXB1dGF0aW9ucyBiYXNlZCBvbiB0aGF0LCB0aGUgZGF0YSBtdXN0IGJlIGNsZWFuZWQuIEl0IGlzIG5lY2Nlc3NhcnkgdG8gaGFuZGxlIG1pc3NpbmcgZGF0YSBhbmQgb3V0bGllcnMuIEJvdGggaGFzIGJlZW4gZW5jb3VudGVyZWQuIE91dGxpZXJzIHdlcmUgaWRlbnRpZmllZCBmb3IgdGhlIG9wZXJhdGl2ZSByZXNlcnZlIG5lZWRzIGluIDIwMTEgYW5kIDIwMTIgKGRhdGEuZnJhbWVzIGBuZWVkcy4yMDExYCBhbmQgYG5lZWRzLjIwMTJgKS4gVGhvc2Ugb3VsdGxpZXJzIGFyZSBjaGFyYWN0ZXJpemVkIGJ5IGNvbnRpbnVvdXMgemVybyB2YWx1ZXMgb3ZlciBhIGxvbmdlciBwZXJpb2Qgb2Ygb2JzZXJ2YXRpb25zLiBUaGlzIHJhZGljYWwgY2hhbmdlIG9mIHJlc2VydmUgcG93ZXIgbmVlZHMgaXMgaW50ZXJwcmV0ZWQgYXMgYW4gb3V0bGllciAocGVyaGFwcyBhIG1lYXNzdXJpbmcgZXJyb3IpLgoKT24gdGhlIG90aGVyIGhhbmQgdGhlcmUgYXJlIHNvbWUgbWlzc2luZyB2YWx1ZXMgZm9yIG9wZXJhdGl2ZSByZXNlcnZlIG5lZWRzIGluIDIwMTIgKGRhdGEuZnJhbWUgYG5lZWRzLjIwMTJgKSBhbmQgdGhlIG9wZXJhdGl2ZSByZXNlcnZlIGNhbGxzIGluIDIwMTEsIDIwMTIgYW5kIDIwMTMgKGRhdGEuZnJhbWVzIGBjYWxscy4yMDExYCwgYGNhbGxzLjIwMTJgIGFuZCBgY2FsbHMuMjAxM2ApLiBUaGUgb3BlcmF0aXZlIG5lZWRzIHZhbHVlcyBpbiAyMDEyIHNob3cgKk5hTiogdmFsdWVzIGZvciBhIGZldyBzcGVjaWZpYyBkYXRlcy4gQWxzbyBzb21lIHJlc2VydmUgY2FsbHMgKG5lZ2F0aXZlIGFuZCBwb3NpdGl2ZSkgb2YgdGhlICpOZXR6cmVnZWx2ZXJidW5kKiBhcmUgbWlzc2luZyBpbiAyMDExLCAyMDEyLCBhbmQgMjAxMy4KCkFsbCB0aG9zZSBkYXRhLmZyYW1lcyBoYXZlIHRvIGJlIHByZS1wcm9jZXNzZWQgYW5kIGNsZWFuZWQuIFRoZSBwcm9jZWR1cmVzIGFyZSBkZXNjcmliZWQgaW4gdGhlIG5leHQgc2VjdGlvbnMuCgo+IElmIHlvdSB3YW50IHRvIGFjaGlldmUgdGhlIHNhbWUgYW5hbHlzaXMgcmVzdWx0cywgaXQgaXMgaW1wb3J0YW50IHRvIGZvbGxvdyB0aGVzZSBzdGVwcyBzaW5jZSB0aGlzIGlzIHRoZSBkYXRhIGJhc2Ugb2YgYWxsIGNhbGN1bGF0aW9ucy4gSXQgaXMgYWxzbyBpbXBvcnRhbnQgdG8gbWVudGlvbiB0aGF0IHdpdGhvdXQgdHJlYXRpbmcgdGhvc2Ugb3V0bGllcnMgYW5kIG1pc3NpbmcgKG9yIE5hTikgdmFsdWVzLCB0aGUgYGdldE1hcmdpbmFsV29ya1ByaWNlcygpYCBvciBgZ2V0T25lTWludXRlQ2FsbHMoKWAgd2lsbCBub3Qgd29yayB3aXRoIHRoZSBjb21wcm9taXNlZCBkYXRhIHNldHMuCgoKCiMjIyMjIFplcm8gdmFsdWVzIGZvciBvcGVyYXRpdmUgcmVzZXJ2ZSBuZWVkcwoKQnkgaW52ZXN0aWdhdGluZyB0aGUgb3BlcmF0aXZlIHJlc2VydmUgbmVlZHMgZm9yIDIwMTEgYW5kIDIwMTIgdGhlcmUgaGF2ZSBiZWVuIG91dGxpZXJzIGRldGVjdGVkLiBUaGVyZSBpcyBhIGNvbnN0YW50IHJvdyBvZiB6ZXJvIHZhbHVlcyBmb3IgdGhlIHJlc2VydmUgbmVlZHMgKGluIE1XKSBzdGFydGluZyBhdCBgMjAxMS0wNy0yNiAxMTowNjo0MGAgdGlsbCBgMjAxMS0wNy0yNiAxMjowMzozMmAgKHdpdGggYW4gZXhjZXB0aW9uYWwgdmFsdWUgb2YgKi0xMjIxLjEzODUqIGF0IGAyMDExLTA3LTI2IDEyOjAzOjAwYCkuIFRoaXMgemVybyByb3cgaXMgcHJlY2VlZGVkIGFuZCBmb2xsb3dlZCBieSBuZWdhdGl2ZSByZXNlcnZlIG5lZWRzIGFyb3VuZCB0aGUgdGhvdXNhbmQgTVcgYXJlYS4gVGhlcmUgYXJlIGFsc28gemVyb3MgaW4gMjAxMiBmb3IgYDIwMTItMDgtMDcgMTA6MTk6NDhgIHRpbGwgIChleGNsdWRpbmcpIGAyMDEyLTA4LTA3IDExOjMzOjEyYC4gVGhlIGFicnVwdCBkZWNsaW5lIGFuZCBsb25nIGhvbGRpbmcgb2YgdGhvc2UgemVybyByb3dzIGluZGljYXRlcyBvdXRsaWVycyAoYm90aCBzb3VyY2VzIGh0dHBzOi8vd3d3LnRyYW5zbmV0YncuZGUvZGUvc3Ryb21tYXJrdC9zeXN0ZW1kaWVuc3RsZWlzdHVuZ2VuL3JlZ2VsZW5lcmdpZS1iZWRhcmYtdW5kLWFicnVmI3NybGJlZGFyZjIwMTEgYW5kIGh0dHA6Ly93d3cuNTBoZXJ0ei5jb20vZGUvTWFlcmt0ZS9SZWdlbGVuZXJnaWUvUmVnZWxlbmVyZ2llLURvd25sb2FkYmVyZWljaCBzaG93IG91dGxpZXJzKS4KCgpgYGB7cn0KCiMgZnVuY3Rpb24gdG8gZ2V0IHRoZSB6ZXJvIG91dGxpZXJzCmNoZWNrRm9yWmVyb091dGxpZXJzIDwtIGZ1bmN0aW9uKGRmKSB7CiAgIyBDdXQgdGhlIDRzZWMgZGF0YSBpbiAxNSBtaW51dGUgYmxvY2tzIHRvIGNvdW50IDAgdmFsdWVzIGZvciBhbGwgYmxvY2tzCiAgZGYkY3V0dGVkVGltZSA8LSBjdXQoZGYkRGF0ZVRpbWUsIGJyZWFrcyA9IHBhc3RlKCIxNSIsICJtaW4iLCBzZXAgPSAiICIpKQogIGRmJGN1dHRlZFRpbWUgPC0gYXMuUE9TSVhjdChkZiRjdXR0ZWRUaW1lLCB0eiA9ICJFdXJvcGUvQmVybGluIikKICAjIENyZWF0ZSBhIHZhcmlhYmxlIHRvIG1hcmsgYSB6ZXJvIE1XIHZhbHVlCiAgZGYkY291bnQgPC0gaWZlbHNlKG5lZWRzLjIwMTEkTVcgPT0gMCwgMSwwKQogIAogICMgQ291bnQgdGhlIHplcm8gbWFya2VycyBieSBzdW1taW5nIGZvciBldmVyeSAxNW1pbiBibG9jawogIHIgPC0gZGYgJT4lCiAgICBncm91cF9ieShjdXR0ZWRUaW1lKSAlPiUKICAgIHN1bW1hcmlzZShuPSBzdW0oY291bnQpKSAlPiUKICAgIGZpbHRlcihuID09IDIyNSkKICAKICByZXR1cm4ocikKfQoKCnplcm9Db3VudHMgPC0gY2hlY2tGb3JaZXJvT3V0bGllcnMobmVlZHMuMjAxMSkKaGVhZCh6ZXJvQ291bnRzKQoKIyBPdXRwdXQ6CiMgICAgICAgICAgICBjdXR0ZWRUaW1lICAgICBuCiMgMSAyMDExLTA3LTI2IDExOjE1OjAwICAgMjI1CiMgMiAyMDExLTA3LTI2IDExOjMwOjAwICAgMjI1CiMgMyAyMDExLTA3LTI2IDExOjQ1OjAwICAgMjI1Cgp6ZXJvQ291bnRzIDwtIGNoZWNrRm9yWmVyb091dGxpZXJzKG5lZWRzLjIwMTIpCmhlYWQoemVyb0NvdW50cykKIyAgICAgICAgICAgIGN1dHRlZFRpbWUgICAgIG4KIyAxIDIwMTItMDgtMDcgMTA6MzA6MDAgICAyMjUKIyAyIDIwMTItMDgtMDcgMTA6NDU6MDAgICAyMjUKIyAzIDIwMTItMDgtMDcgMTE6MDA6MDAgICAyMjUKIyA0IDIwMTItMDgtMDcgMTE6MTU6MDAgICAyMjUKYGBgCgpUbyByZXBsYWNlIG9yIGltcHV0ZSB0aG9zZSB6ZXJvIHZhbHVlcyB3ZSBjb3VsZCBzaW1wbHkgdGFrZSB0aGUgZGF0YSBvZiB0aGUgKHR3bykgeWVhciBiZWZvcmUgb24gYDIwMTAtMDctMjZgIChgMjAxMC0wOC0wN2ApLiBJIHdpbGwgc2hvdyB0aGUgcmVwbGFjZW1lbnQgcHJvY2VkdXJlIGZvciB0aGUgZGF0YSBvZiAyMDExLiBUaGVyZWZvcmUgd2Ugd2lsbCBjb21wYXJlICBzb21lIHN0YXRpc3RpY3Mgd2l0aCB0aGUgb2JzZXJ2YXRpb25zIG9mIGFuIGhvdXIgYmVmb3JlIGFuZCBhZnRlciB0aGUgb3V0bGllcnMuIAoKYGBge3J9CgojIEdldCBvcHJhdGl2ZSByZXNlcnZlIG5lZWRzIG9mIHRoZSB5ZWFyIGJlZm9yZQpuLjIwMTAgPSBnZXRSZXNlcnZlTmVlZHMoJzI2LjA3LjIwMTAnLCAnMjYuMDcuMjAxMCcpCgpuLjIwMTEuaG91ci5iZWZvcmUgPC0gbmVlZHMuMjAxMVtuZWVkcy4yMDExJERhdGVUaW1lID49IGFzLlBPU0lYY3QoIjIwMTEtMDctMjYgMTA6MDA6MDAiKSAmIG5lZWRzLjIwMTEkRGF0ZVRpbWUgPCBhcy5QT1NJWGN0KCIyMDExLTA3LTI2IDExOjAwOjAwIiksXQpuLjIwMTEuaG91ci5hZnRlciA8LSBuZWVkcy4yMDExW25lZWRzLjIwMTEkRGF0ZVRpbWUgPj0gYXMuUE9TSVhjdCgiMjAxMS0wNy0yNiAxMjowMDowMCIpICYgbmVlZHMuMjAxMSREYXRlVGltZSA8IGFzLlBPU0lYY3QoIjIwMTEtMDctMjYgMTM6MDA6MDAiKSxdCm4uMjAxMC5ob3VyLmF0IDwtIG4uMjAxMFtuLjIwMTAkRGF0ZVRpbWUgPj0gYXMuUE9TSVhjdCgiMjAxMC0wNy0yNiAxMTowNjo0MCIpICYgbi4yMDEwJERhdGVUaW1lIDwgYXMuUE9TSVhjdCgiMjAxMC0wNy0yNiAxMjowMzozMiIpLF0KCnN1bW1hcnkobi4yMDExLmhvdXIuYmVmb3JlJE1XKQojICAgIE1pbi4gMXN0IFF1LiAgTWVkaWFuICAgIE1lYW4gM3JkIFF1LiAgICBNYXguIAojIC0xNTc4LjEgIC05NjEuNSAgLTc1My44ICAtNzk1LjYgIC02MDAuMCAgLTI3MC42IAogICAKc3VtbWFyeShuLjIwMTEuaG91ci5hZnRlciRNVykKIyAgICBNaW4uIDFzdCBRdS4gIE1lZGlhbiAgICBNZWFuIDNyZCBRdS4gICAgTWF4LiAKIyAtMTU5Ny41IC0xMTU0LjYgIC05MTAuNyAgLTg4MS4wICAtNjk3LjcgICAgIDAuMCAKCnN1bW1hcnkobi4yMDEwLmhvdXIuYXQkTVcpCiMgICAgIE1pbi4gIDFzdCBRdS4gICBNZWRpYW4gICAgIE1lYW4gIDNyZCBRdS4gICAgIE1heC4gCiMgLTE4NTkuMTkgIC0yODQuMzggICAtNzIuMDIgIC0xMzkuNDQgICAgNjAuMTcgICAzNzkuMzIgIAoKYGBgCgpUaGUgc3RhdGlzdGljcyBzaG93IHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBpcyBxdWl0ZSBkaWZmZXJlbnQuIFRoZSBtZWFuIGFuZCBtZWRpYW4gaXMgbG93ZXIuIFRoZXJlIGFyZSBhbHNvIHBvc2l0aXZlIHJlc2VydmUgbmVlZHMgaW4gMjAxMC4gVG8gZ2FpbiBhIGJldHRlciBpbnNpZ2h0IG9mIHRoZSBkaXN0cmlidXRpb24sIHdlIHdpbGwgcGxvdCB0aGUgZGF0YSB3aXRoIHRoZSBvdXRsaWVycyBhbmQgdGhlbiB3aXRoIHRoZSByZXBsYWNlZCAyMDEwIGRhdGEuIFRoZSBmaWd1cmUgaW4gdGhlIGNvZGUgc25pcHBldCBiZWxvdyBzaG93cyB0aGUgb3V0bGllcnMuCgpgYGB7cn0KIyBob3VyIGJlZm9yZSBhbmQgYWZ0ZXIgdGhlIGhvdXIgd2l0aCB0aGUgemVybyB2YWx1ZXMKbi4yMDExLmhvdXIgPC0gbmVlZHMuMjAxMVtuZWVkcy4yMDExJERhdGVUaW1lID49IGFzLlBPU0lYY3QoIjIwMTEtMDctMjYgMTA6MDA6MDAiKSAmIG5lZWRzLjIwMTEkRGF0ZVRpbWUgPCBhcy5QT1NJWGN0KCIyMDExLTA3LTI2IDEzOjAwOjAwIiksXQoKIyBQbG90IHRoZSBvdXRsaWVycyBpbiBjb250ZXh0IG9mIHRoZSBob3VyIGJlZm9yZSBhbmQgYWZ0ZXIKbGlicmFyeShnZ3Bsb3QyKQpnZ3Bsb3QoZGF0YSA9IG4uMjAxMS5ob3VyLCBhZXMoeD1EYXRlVGltZSwgeSA9IE1XKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlPSJaZXJvIE91dGxpZXJzIEZvciBPcHJlYXRpdmUgUmVzZXJ2ZSBOZWVkcyBvbiAyMDExLTA3LTI2IikKYGBgCgpOb3cgaXQgaXMgdGltZSB0byBmaXQgaW4gdGhlIDIwMTAgZGF0YS4KCmBgYHtyfQojIFJlcGxhY2UgdGhlIHplcm8gdmFsdWVzIHdpdGggdGhlIGRhdGEgb2YgdGhlIHNhbWUgdGltZSBwZXJpb2QgaW4gMjAxMApuLjIwMTEuaG91cltuLjIwMTEuaG91ciREYXRlVGltZSA+PSBhcy5QT1NJWGN0KCIyMDExLTA3LTI2IDExOjA2OjQwIikgJiBuLjIwMTEuaG91ciREYXRlVGltZSA8IGFzLlBPU0lYY3QoIjIwMTEtMDctMjYgMTI6MDM6MzIiKSxdJE1XIDwtIG4uMjAxMFtuLjIwMTAkRGF0ZVRpbWUgPj0gYXMuUE9TSVhjdCgiMjAxMC0wNy0yNiAxMTowNjo0MCIpICYgbi4yMDEwJERhdGVUaW1lIDwgYXMuUE9TSVhjdCgiMjAxMC0wNy0yNiAxMjowMzozMiIpLF0kTVcKCiMgUGxvdCB0aGUgcmVwbGFjZWQgZGF0YQpnZ3Bsb3QoZGF0YSA9IG4uMjAxMS5ob3VyLCBhZXMoeD1EYXRlVGltZSwgeSA9IE1XKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlPSJSZXBsYWNlZCBaZXJvIE91dGxpZXJzIFdpdGggT3ByZWF0aXZlIFJlc2VydmUgTmVlZHMgb2YgMjAxMCIpCmBgYAoKQXQgdGhlIGJlZ2luaW5nIHRoZXJlIGlzIGEgc3Bpa2UgZnJvbSBhcm91bmQgKi0xMDAwKiB0byAqLTMwMCogYW5kIHRoZW4gdGhlIDIwMTAgZGF0YSBmYWxscyBiYWNrIHRvIGFyb3VuZCAqLTE4MDAqLiBUaGlzIGluZGljYXRlcyBzb21lIHR1cmJ1bGFuY2VzLiBPbmUgY291bGQgYXJndWUgYW5kIHRyeSB0byBtYWtlIHRoaXMgdHJhbnNpdGlvbiBtb3JlIHNtb290aGx5LiBTbyB3ZSB3aWxsIG1vZGlmeSB0aGUgZGF0YSBhIGJpdCB0byBzbW9vdGggdGhlIGV4dHJlbWUgdmFsdWVzIGFuZCB0byBnZXQgY2xvc2VyIHRvIHRoZSBzdGF0aXN0aWNzIChzZWUgaW4gdGhlIHNlY3Rpb24gYWJvdmUpIG9mIHRoZSBob3VycyBiZWZvcmUgYW5kIGFmdGVyLiAKCmBgYHtyfQojIFJlcGxhY2UgdGhlIHplcm8gdmFsdWVzIHdpdGggdGhlIGRhdGEgb2YgdGhlIHNhbWUgdGltZSBwZXJpb2QgaW4gMjAxMApuMTAgPC0gbi4yMDEwW24uMjAxMCREYXRlVGltZSA+PSBhcy5QT1NJWGN0KCIyMDEwLTA3LTI2IDExOjA2OjQwIikgJiBuLjIwMTAkRGF0ZVRpbWUgPCBhcy5QT1NJWGN0KCIyMDEwLTA3LTI2IDEyOjAzOjMyIiksXQpuMTAkTVcgPC0gaWZlbHNlKG4xMCRNVyA+PSAtMTIwMCwgbjEwJE1XIC0gNDAwLCBuMTAkTVcgKyA0MDApCnN1bW1hcnkobjEwJE1XKQoKCm4uMjAxMS5ob3VyW24uMjAxMS5ob3VyJERhdGVUaW1lID49IGFzLlBPU0lYY3QoIjIwMTEtMDctMjYgMTE6MDY6NDAiKSAmIG4uMjAxMS5ob3VyJERhdGVUaW1lIDwgYXMuUE9TSVhjdCgiMjAxMS0wNy0yNiAxMjowMzozMiIpLF0kTVcgPC0gbjEwJE1XCgojIFBsb3QgdGhlIG1vZGlmaWVkIHJlcGxhY2luZyBkYXRhCmdncGxvdChkYXRhID0gbi4yMDExLmhvdXIsIGFlcyh4PURhdGVUaW1lLCB5ID0gTVcpKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGU9IlJlcGxhY2VkIFplcm8gT3V0bGllcnMgV2l0aCBNb2RpZmllZCBPcGVyYXRpdmUgUmVzZXJ2ZSBOZWVkcyBvZiAyMDEwIikKYGBgCgpUaGlzIGxvb2tzIHF1aXRlIHNtb290aC4gSXQgY2FwdHVyZXMgdGhlIHVwdHJlbmQgYW5kIGRvd250cmVuZCBhdCBhcm91bmQgYDExOjMwOjAwYCBhbmQgYWxzbyB0aGUgdW5pcXVlIHNwaWtlIGF0IHRoZSBiZWdpbmluZy4gVGhlIHdob2xlIHNlcmllcyBsaWVzIGJlbG93IHplcm8uIE5vdyB3ZSB3aWxsIHJlcGxhY2UgdGhvc2UgdmFsdWVzIHdpdGggdGhlIG9yaWdpbmFsIGRhdGEgc2V0IG9mIHRoZSBvcGVyYXRpdmUgcmVzZXJ2ZSBuZWVkcyBpbiAyMDExIChgbmVlZHMuMjAxMWApLgoKCmBgYHtyfQoKIyBSZXBsYWNlIHRoZSAwIGRhdGEgKG91dGxpZXJzKSBpbiB0aGUgMjAxMSBkYXRhIHNldCB3aXRoIHRoZSB2YWx1ZXMgb2YgMjAxMApuZWVkcy4yMDExW25lZWRzLjIwMTEkRGF0ZVRpbWUgPj0gYXMuUE9TSVhjdCgiMjAxMS0wNy0yNiAxMTowNjo0MCIpICYgbmVlZHMuMjAxMSREYXRlVGltZSA8IGFzLlBPU0lYY3QoIjIwMTEtMDctMjYgMTI6MDM6MzIiKSxdJE1XIDwtIG4xMCRNVwoKIyBTYW5pdHkgY2hlY2sgdGhhdCBvdXRsaWVycyBhcmUgcmVtb3ZlZApoZWFkKGNoZWNrRm9yWmVyb091dGxpZXJzKG5lZWRzLjIwMTEpKQojIDAgcm93cwpgYGAKClRoZSBzYW1lIHdhcyBkb25lIHdpdGggdGhlIGRhdGEgb2YgMjAxMi4gVGhlIGdyYXBoaWMgYmVsb3cgc2hvd3MgdGhlIGZpdHRlZCAqaG9sZSouIFRoZSBkYXRhIG9mIDIwMTAgd2FzIG1vZGlmaWVkIGJ5IHJldmVyc2luZyBpdHMgb3JkZXIgKHN1Y2ggdGhhdCBpdCBmb2xsb3dlZCB0aGUgdXB0cmVuZCkgYW5kIGFkZGluZyA0MDAgTVcuCgohW1JlcGxhY2luZyB6ZXJvIG91dGxpZXJzIHZhbHVlcyBmb3IgMjAxNy0wOC0wN10oemVyb18wN18wOC5wbmcpeyB3aWR0aD0xMDAlIH0KCgoKIyMjIyMgTmFOIHZhbHVlcyBmb3Igb3BlcmF0aXZlIHJlc2VydmUgbmVlZHMgaW4gMjAxMgoKQm90aCBzb3VyY2VzIGh0dHBzOi8vd3d3LnRyYW5zbmV0YncuZGUvZGUvc3Ryb21tYXJrdC9zeXN0ZW1kaWVuc3RsZWlzdHVuZ2VuL3JlZ2VsZW5lcmdpZS1iZWRhcmYtdW5kLWFicnVmI3NybGJlZGFyZjIwMTEgYW5kIGh0dHA6Ly93d3cuNTBoZXJ0ei5jb20vZGUvTWFlcmt0ZS9SZWdlbGVuZXJnaWUvUmVnZWxlbmVyZ2llLURvd25sb2FkYmVyZWljaCBzaG93IE5hTiB2YWx1ZXMgb24gdGhlIGRhdGVzIGAyMDEyLTA3LTA0YCBgMjAxMi0wNy0wNWAgYDIwMTItMDctMDZgIGFuZCBgMjAxMi0wNy0wN2AuCgpgYGB7cn0KbmFucyA8LSBmaWx0ZXIobmVlZHMuMjAxMiwgTVcgPT0gIk5hTiIpCnVuYW5zIDwtIHVuaXF1ZShmb3JtYXQobmFucyREYXRlVGltZSwgIiVZLSVtLSVkIikpCnVuYW5zCiMgIjIwMTItMDctMDQiICIyMDEyLTA3LTA1IiAiMjAxMi0wNy0wNiIgIjIwMTItMDctMDciCgojIFNpZGUgTm90ZToKIyBUaW1lIHBlcmlvZHMgZm9yIHRoZSBOYU4gdmFsdWVzCiMKIyAiMjAxMi0wNy0wNCAyMTo0NTowNCBDRVNUIiAtICIyMDEyLTA3LTA0IDIxOjUwOjM2IENFU1QiCiMgIjIwMTItMDctMDUgMjE6NDU6MDggQ0VTVCIgLSAiMjAxMi0wNy0wNSAyMjowMToxMiBDRVNUIgojICIyMDEyLTA3LTA2IDIxOjQ1OjA0IENFU1QiIC0gIjIwMTItMDctMDYgMjE6NTI6MTIgQ0VTVCIKIyAiMjAxMi0wNy0wNyAyMTo0NTowNCBDRVNUIiAtICIyMDEyLTA3LTA3IDIxOjUwOjUyIENFU1QiCgpgYGAKCgpOZXh0IHdlIHdpbGwgbG9vayBhdCB0aGUgZGF0YSB3aXRoIHRoZSBOYU4gdmFsdWVzIGZvciBvbmUgKGAyMDEyLTA3LTA1YCkgb2YgdGhlIGZvdXIgZGF0ZXMuIFRoaXMgc2hvd3MgdGhlIGFwcHJvYWNoIGhvdyB0aGUgcmVzdCB3aWxsIGJlIHJlcGxhY2VkIHdpdGggdGhlIGRhdGEgb2YgdGhlIHllYXIgMjAxMC4gVGhlIGRhdGEgb2YgMjAxMSBpcyBub3QgdGFrZW4sIHNpbmNlIHRoaXMgY291bGQgYmlhcyB0aGUgYW5hbHlzaXMgYXMgaXQgaXMgcGFydCBvZiB0aGUgd2hvbGUgZGF0YSBzZXQuIE9wdGlvbmFsLCBzb21lIHZhbHVlcyB3aWxsIGJlIGFkanVzdGVkIHRvIGZpdCBtb3JlIHNtb290aGx5IGludG8gdGhlIHRpbWUgc2VyaWVzLiBUaGVyZWZvcmUgd2Ugd2lsbCB2aXN1YWxpemUgdGhlIHRpbWUgc2VyaWVzIGRhdGEgd2l0aCBhIGZldyB2YWx1ZXMgYmVmb3JlIGFuZCBhZnRlciB0aGUgTmFOIHZhbHVlcyBkZXBlbmRpbmcgb24gdGhlIHJhbmdlIG9mIHRoZSBOYU4gdmFsdWVzLgpgYGB7cn0KIyBHZXQgdGhlIG5lZWRlZCBkYXRhIG9mIDIwMTAKbi4yMDEwIDwtIGdldFJlc2VydmVOZWVkcygiMDQuMDcuMjAxMCIsIjA3LjA3LjIwMTAiKQojIHNldCB0aGUgZGF0ZSwgZS5nLiAyMDEyLTA3LTA1CmRhdGUgPC0gMiAKIyBnZXQgdGhlIHRpbWUgcGVyaW9kIG9mIHRoZSBOYU4gdmFsdWVzCm1pbmkgPC0gbWluKG5hbnNbZm9ybWF0KG5hbnMkRGF0ZVRpbWUsICIlWS0lbS0lZCIpID09IGFzLkRhdGUodW5hbnNbZGF0ZV0pLCAiRGF0ZVRpbWUiXSkKbWF4aSA8LSBtYXgobmFuc1tmb3JtYXQobmFucyREYXRlVGltZSwgIiVZLSVtLSVkIikgPT0gYXMuRGF0ZSh1bmFuc1tkYXRlXSksICJEYXRlVGltZSJdKQoKbWkgPC0gbWluaSAtIChtYXhpIC0gbWluaSkgIyBnZXQgdGhlIGRhdGUgZm9yIGEgZmV3IG1pbnV0ZXMgYmVmb3JlCm1hIDwtIG1heGkgKyAobWF4aSAtIG1pbmkpICMgZ2V0IHRoZSBkYXRlIGZvciBhIGZldyBtaW51dGVzIGFmdGVyCgoKIyBnZXQgdGhlIGRhdGEgYSBmZXcgbWludXRlcyAocmFuZ2Ugb2YgbWF4IGFuZCBtaW4pIGJlZm9yZSBhbmQgYWZ0ZXIgdGhlIE5hTiB2YWx1ZXMKbiA8LSBuZWVkcy4yMDEyW25lZWRzLjIwMTIkRGF0ZVRpbWUgPj0gbWkgJiBuZWVkcy4yMDEyJERhdGVUaW1lIDw9IG1hLF0KCmBgYAoKCk5vdyBsZXQgdXMgcGxvdCB0aGUgdGltZSBzZXJpZXMgZGF0YSB3aXRoIHRoZSBOYU4gKmhvbGUqIHdoaWNoIGhhcyB0byBiZSBmaWxsZWQgd2l0aCB0aGUgZGF0YSBvZiAyMDEwLgpgYGB7cn0KIyBwbG90IHRoZSBkYXRhIHdpdGggdGhlIE5hTiBob2xlCmdncGxvdChkYXRhID0gbiwgYWVzKHg9RGF0ZVRpbWUsIHkgPSBNVykpICsKICBnZW9tX2xpbmUoKSArCiAgbGFicyh0aXRsZSA9IHBhc3RlKCJOYU4gVmFsdWVzIEZvciBPcGVyYXRpdmUgUmVzZXJ2ZSBOZWVkcyBvbiAiLCB1bmFuc1tkYXRlXSwgc2VwPSIiKSkKCmBgYAoKVGhlIG5leHQgc3RlcCBpcyB0byBnZXQgdGhlIGRhdGEgb2YgMjAxMCBhbmQgcmVwbGFjZSB0aGUgTmFOIHZhbHVlcy4KYGBge3J9CiMgZ2V0IHRoZSBkYXRlcyBvZiB0aGUgbGFzdCB5ZWFyCm1pbnkgPC0gc2VxKG1pbmksIGxlbmd0aD0yLCBieT0iLTIgeWVhcnMiKVsyXQptYXh5IDwtIHNlcShtYXhpLCBsZW5ndGg9MiwgYnk9Ii0yIHllYXJzIilbMl0KIyBnZXQgdGhlIGRhdGEgb2YgdGhlIGxhc3QgeWVhcgpuciA8LSBuLjIwMTBbbi4yMDEwJERhdGVUaW1lID49IG1pbnkgJiBuLjIwMTAkRGF0ZVRpbWUgPD0gbWF4eSxdCgojIHJlcGxhY2UgdGhlIE5hTiB2YWx1ZXMgd2l0aCB0aGF0IG9mIHRoZSBsYXN0IHllYXIKbltuJERhdGVUaW1lID49IG1pbmkgJiBuJERhdGVUaW1lIDw9IG1heGksXSRNVyA8LSBuciRNVwpgYGAKCgpBZ2FpbiwgdGhlIHBsb3Qgd2l0aCB0aGUgcmVwbGFjZWQgZGF0YSBnaXZlcyB1cyBhbiBpbnNpZ2h0IG9uIGhvdyB3ZWxsIHRoZSBkYXRhIGZpdHMgaW50byB0aGUgdGltZSBzZXJpZXMuCmBgYHtyfQojIHBsb3QgdGhlIGRhdGEgd2l0aCB0aGUgZmlsbGVkIGhvbGUKZ2dwbG90KGRhdGEgPSBuLCBhZXMoeD1EYXRlVGltZSwgeSA9IE1XKSkgKwogIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlID0gcGFzdGUoIlJlcGxhY2VkIE5hTiBWYWx1ZXMgRm9yIE9wZXJhdGl2ZSBSZXNlcnZlIE5lZWRzIG9uICIsIHVuYW5zW2RhdGVdLCBzZXA9IiIpKQpgYGAKCkhlcmUsIHRoZSBkYXRhIGluIDIwMTAgc2VlbXMgdG8gYmUgYSBiaXQgbG93IGFuZCB0aGUgdHJhbnNpdGlvbnMgYXQgdGhlIGVuZCBpcyBhIGJpdCB0b28gc3RlZXAuIEhlbmNlLCB3ZSB3aWxsIGFkanVzdCB0aG9zZSB2YWx1ZXMgYnkgYWRkaW5nIGEgY29uc3RhbnQgdmFsdWUgb2YgMjAwIE1XLgpgYGB7cn0KIyBPcHRpb25hbCEhCiMgTW9kaWZ5IGFuZCBhZGp1c3QgdGhlIHJlcGxhY2luZyB2YWx1ZXMKbnIkTVcgPC0gbnIkTVcgKyAyMDAKCiMgcmVwbGFjZSB0aGUgYWRqdXN0ZWQgdmFsdWVzIHdpdGggdGhhdCBvZiB0aGUgbGFzdCB5ZWFyCm5bbiREYXRlVGltZSA+PSBtaW5pICYgbiREYXRlVGltZSA8PSBtYXhpLF0kTVcgPC0gbnIkTVcKYGBgCgoKVGhpcyB0aW1lIHRoZSBkYXRhIGZpdHMgbW9yZSBzbW9vdGhseS4gVGhlcmUgaXMgc3RpbGwgYSBzdGVlcCBhc2NlbnQgb2YgcmVzZXJ2ZSBuZWVkcyBhdCB0aGUgYmVnaW5pbmcgYW5kIGVuZCwgYnV0IHRoaXMgaXMgYWNjZXB0YWJsZS4KYGBge3J9CiMgcGxvdCB0aGUgZGF0YSB3aXRoIHRoZSBmaWxsZWQgaG9sZQpnZ3Bsb3QoZGF0YSA9IG4sIGFlcyh4PURhdGVUaW1lLCB5ID0gTVcpKSArCiAgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGUgPSBwYXN0ZSgiQWRqdXN0ZWQgUmVwbGFjZSBWYWx1ZXMgRm9yIE9wZXJhdGl2ZSBSZXNlcnZlIE5lZWRzIG9uICIsIHVuYW5zW2RhdGVdLCBzZXA9IiIpKQpgYGAKCklmIHRoZSBtb2RpZmljYXRpb24gaXMgZW5vdWdoLCB0aGUgZGF0YSAoYG5yYCBkYXRhLmZyYW1lIGhvbGRzIHRoZSByZXBsYWNpbmcgdmFsdWVzKSBjYW4gYmUgcmVwbGFjZWQgZm9yIHRoZSAqb3JpZ2luYWwqIG9wZXJhdGl2ZSBuZWVkcyBkYXRhIHNldCBvZiAyMDEyLgpgYGB7cn0KIyByZXBsYWNlIE5hTiBWQUxVRVMgT0YgdGhlIG9yaWdpbmFsIGRhdGEgc2V0IG5lZWRzLjIwMTIKbmVlZHMuMjAxMltuZWVkcy4yMDEyJERhdGVUaW1lID49IG1pbmkgJiBuZWVkcy4yMDEyJERhdGVUaW1lIDw9IG1heGksXSRNVyA8LSBuciRNVwpgYGAKClRoZSBmb2xsb3dpbmcgZ3JhcGhzIHNob3cgdGhlIHJlc3BlY3RpdmUgcmVwbGFjZW1lbnQgZm9yIHRoZSBvdGhlciB0aHJlZSBkYXRlcy4gVGhlcmUgd2VyZSBhbHNvIHNvbWUgYWRqdXN0bWVudCBtYWRlOgoKLSBGb3IgYDIwMTItMDctMDRgOiBgbnIkTVcgPSBuciRNVyAtIDkwMGAKLSBGb3IgYDIwMTItMDctMDZgOiBgbnIkTVcgPSBpZmVsc2UobnIkTVcgPCAtMTI1MCwgbnIkTVcgKyA4MDAsIG5yJE1XICsgMzAwKWAKLSBGb3IgYDIwMTItMDctMDdgOiBgbnIkTVcgPSBuciRNVyArIDEyMDBgCgohW1JlcGxhY2luZyBOYU4gdmFsdWVzIGZvciAyMDE3LTA3LTA0XShuYW5fMDQucG5nKXsgd2lkdGg9MTAwJSB9CgohW1JlcGxhY2luZyBOYU4gdmFsdWVzIGZvciAyMDE3LTA3LTA2XShuYW5fMDYucG5nKXsgd2lkdGg9MTAwJSB9CgohW1JlcGxhY2luZyBOYU4gdmFsdWVzIGZvciAyMDE3LTA3LTA3XShuYW5fMDcucG5nKXsgd2lkdGg9MTAwJSB9CgoKCgojIyMjIyBNaXNzaW5nIFZhbHVlcyBmb3Igb3BlcmF0aXZlIHJlc2VydmUgY2FsbHMKCk1pc3NpbmcgdmFsdWVzIG9jY3VyIGZvciAxNSBtaW51dGUgY2FsbHMgb2YgbmVnYXRpdmUgYW5kIHBvc2l0aXZlIFNSIG9mIHRoZSAqTmV0enJlZ2VsdmVyYnVuZCogaW4gMjAxMywgMjAxMiBhbmQgMjAxMToKCi0gKioyMDEzOioqIFRoZXJlIGFyZSAzNyBvYnNlcnZhdGlvbnMgaW4gMjAxMyB3aXRoIG1pc3NpbmcgdmFsdWVzLiBUaGlzIGlzIGFyb3VuZCAwLjExJS4gVGhlIG1haW4gZGF0ZSBpcyBgMjAxMy0xMi0wNGAuIEF0IGAxNTo0NWAgdGlsbCB0aGUgZW5kIG9mIHRoZSBkYXkgdmFsdWVzIGZvciBuZWdhdGl2ZSBhbmQgcG9zaXRpdmUgU1IgY2FsbHMgYXJlIG1pc3NpbmcuIFRoZW4gdGhlcmUgYXJlIGZvdXIgb3RoZXIgZGF5cyAoYDIwMTMtMDQtMDNgLCBgMjAxMy0wNi0wNGAsIGAyMDEzLTA5LTE4YCBhbmQgYDIwMTMtMTItMDdgKSB3aXRoIG9uZSBvYnNlcnZhdGlvbiBlYWNoLgoKLSAqKjIwMTI6KiogVGhlcmUgYXJlIDM4IG9ic2VydmF0aW9ucyBpbiAyMDEyIHdpdGggbWlzc2luZyB2YWx1ZXMuIFRoaXMgaXMgYXJvdW5kIDAuMTElLiBUaGUgbWFpbiBkYXRlcyBhcmUgYDIwMTItMDItMjZgIGFuZCBgMjAxMi0xMC0yN2AuIHdpdGggbWlzc2luZyB2YWx1ZXMgZm9yIG5lZ2F0aXZlIGFuZCBwb3NpdGl2ZSBTUiBjYWxscyBpbiB0aGUgZXZlbmluZyBhbmQgbW9ybmluZyBob3Vycy4gVGhlbiB0aGVyZSBhcmUgdHdvIG90aGVyIGRheXMgKGAyMDEyLTA4LTA1YCwgYDIwMTItMDktMDlgKSB3aXRoIG9uZSBvYnNlcnZhdGlvbiBlYWNoLgoKLSAqKjIwMTE6KiogVGhlcmUgYXJlIDIzNSBvYnNlcnZhdGlvbnMgaW4gMjAxMSAob25seSBoYWxmIHRoZSB5ZWFyLCBzdGFydGluZyBmcm9tIGAyMDExLTA3LTAxYCkgd2l0aCBtaXNzaW5nIHZhbHVlcy4gVGhpcyBpcyBhcm91bmQgMS4zMyUuIFRoZSBkYXRlcyBhcmUgdG9vIG11Y2ggdG8gbGlzdCB0aGVtLgoKCgpgYGB7cn0KIyBGT1IgMjAxMwpucm93KGNhbGxzLjIwMTNbaXMubmEoY2FsbHMuMjAxMyRuZWdfTVcpLCBdKQojIFsxXSAzNyAtLT4gY2EuIDAuMTElCmNhbGxzLjIwMTNbaXMubmEoY2FsbHMuMjAxMyRuZWdfTVcpLCAiRGF0ZVRpbWUiXQojIFsxXSAiMjAxMy0wNC0wMyAxOTowMDowMCBDRVNUIiAiMjAxMy0wNi0wNCAxNTowMDowMCBDRVNUIgojIFszXSAiMjAxMy0wOS0xOCAwODozMDowMCBDRVNUIiAiMjAxMy0xMi0wNCAxNTo0NTowMCBDRVQiIAojIFs1XSAiMjAxMy0xMi0wNCAxNjowMDowMCBDRVQiICAiMjAxMy0xMi0wNCAxNjoxNTowMCBDRVQiIAojIFs3XSAiMjAxMy0xMi0wNCAxNjozMDowMCBDRVQiICAiMjAxMy0xMi0wNCAxNjo0NTowMCBDRVQiIAojIFs5XSAiMjAxMy0xMi0wNCAxNzowMDowMCBDRVQiICAiMjAxMy0xMi0wNCAxNzoxNTowMCBDRVQiIAojIFsxMV0gIjIwMTMtMTItMDQgMTc6MzA6MDAgQ0VUIiAgIjIwMTMtMTItMDQgMTc6NDU6MDAgQ0VUIiAKIyBbMTNdICIyMDEzLTEyLTA0IDE4OjAwOjAwIENFVCIgICIyMDEzLTEyLTA0IDE4OjE1OjAwIENFVCIgCiMgWzE1XSAiMjAxMy0xMi0wNCAxODozMDowMCBDRVQiICAiMjAxMy0xMi0wNCAxODo0NTowMCBDRVQiIAojIFsxN10gIjIwMTMtMTItMDQgMTk6MDA6MDAgQ0VUIiAgIjIwMTMtMTItMDQgMTk6MTU6MDAgQ0VUIiAKIyBbMTldICIyMDEzLTEyLTA0IDE5OjMwOjAwIENFVCIgICIyMDEzLTEyLTA0IDE5OjQ1OjAwIENFVCIgCiMgWzIxXSAiMjAxMy0xMi0wNCAyMDowMDowMCBDRVQiICAiMjAxMy0xMi0wNCAyMDoxNTowMCBDRVQiIAojIFsyM10gIjIwMTMtMTItMDQgMjA6MzA6MDAgQ0VUIiAgIjIwMTMtMTItMDQgMjA6NDU6MDAgQ0VUIiAKIyBbMjVdICIyMDEzLTEyLTA0IDIxOjAwOjAwIENFVCIgICIyMDEzLTEyLTA0IDIxOjE1OjAwIENFVCIgCiMgWzI3XSAiMjAxMy0xMi0wNCAyMTozMDowMCBDRVQiICAiMjAxMy0xMi0wNCAyMTo0NTowMCBDRVQiIAojIFsyOV0gIjIwMTMtMTItMDQgMjI6MDA6MDAgQ0VUIiAgIjIwMTMtMTItMDQgMjI6MTU6MDAgQ0VUIiAKIyBbMzFdICIyMDEzLTEyLTA0IDIyOjMwOjAwIENFVCIgICIyMDEzLTEyLTA0IDIyOjQ1OjAwIENFVCIgCiMgWzMzXSAiMjAxMy0xMi0wNCAyMzowMDowMCBDRVQiICAiMjAxMy0xMi0wNCAyMzoxNTowMCBDRVQiIAojIFszNV0gIjIwMTMtMTItMDQgMjM6MzA6MDAgQ0VUIiAgIjIwMTMtMTItMDQgMjM6NDU6MDAgQ0VUIiAKIyBbMzddICIyMDEzLTEyLTA3IDIwOjAwOjAwIENFVCIKCiMgRk9SIDIwMTIKbnJvdyhjYWxscy4yMDEyW2lzLm5hKGNhbGxzLjIwMTIkbmVnX01XKSwgXSkKIyBbMV0gMzggLS0+IGNhLiAwLjExJQpjYWxscy4yMDEyW2lzLm5hKGNhbGxzLjIwMTIkbmVnX01XKSwgIkRhdGVUaW1lIl0KIyBbMV0gIjIwMTItMDItMjYgMjA6MzA6MDAgQ0VUIiAgIjIwMTItMDItMjYgMjA6NDU6MDAgQ0VUIiAKIyBbM10gIjIwMTItMDItMjYgMjE6MDA6MDAgQ0VUIiAgIjIwMTItMDItMjYgMjE6MTU6MDAgQ0VUIiAKIyBbNV0gIjIwMTItMDItMjYgMjE6MzA6MDAgQ0VUIiAgIjIwMTItMDItMjYgMjE6NDU6MDAgQ0VUIiAKIyBbN10gIjIwMTItMDItMjYgMjI6MDA6MDAgQ0VUIiAgIjIwMTItMDItMjYgMjI6MTU6MDAgQ0VUIiAKIyBbOV0gIjIwMTItMDItMjYgMjI6MzA6MDAgQ0VUIiAgIjIwMTItMDItMjYgMjI6NDU6MDAgQ0VUIiAKIyBbMTFdICIyMDEyLTAyLTI2IDIzOjAwOjAwIENFVCIgICIyMDEyLTAyLTI2IDIzOjE1OjAwIENFVCIgCiMgWzEzXSAiMjAxMi0wMi0yNiAyMzozMDowMCBDRVQiICAiMjAxMi0wMi0yNiAyMzo0NTowMCBDRVQiIAojIFsxNV0gIjIwMTItMDgtMDUgMDA6MDA6MDAgQ0VTVCIgIjIwMTItMDktMDkgMDA6MDA6MDAgQ0VTVCIKIyBbMTddICIyMDEyLTEwLTI3IDAwOjAwOjAwIENFU1QiICIyMDEyLTEwLTI3IDAwOjE1OjAwIENFU1QiCiMgWzE5XSAiMjAxMi0xMC0yNyAwMDozMDowMCBDRVNUIiAiMjAxMi0xMC0yNyAwMDo0NTowMCBDRVNUIgojIFsyMV0gIjIwMTItMTAtMjcgMDE6MDA6MDAgQ0VTVCIgIjIwMTItMTAtMjcgMDE6MTU6MDAgQ0VTVCIKIyBbMjNdICIyMDEyLTEwLTI3IDAxOjMwOjAwIENFU1QiICIyMDEyLTEwLTI3IDAxOjQ1OjAwIENFU1QiCiMgWzI1XSAiMjAxMi0xMC0yNyAwMjowMDowMCBDRVNUIiAiMjAxMi0xMC0yNyAwMjoxNTowMCBDRVNUIgojIFsyN10gIjIwMTItMTAtMjcgMDI6MzA6MDAgQ0VTVCIgIjIwMTItMTAtMjcgMDI6NDU6MDAgQ0VTVCIKIyBbMjldICIyMDEyLTEwLTI3IDAzOjAwOjAwIENFU1QiICIyMDEyLTEwLTI3IDAzOjE1OjAwIENFU1QiCiMgWzMxXSAiMjAxMi0xMC0yNyAwMzozMDowMCBDRVNUIiAiMjAxMi0xMC0yNyAwMzo0NTowMCBDRVNUIgojIFszM10gIjIwMTItMTAtMjcgMDQ6MDA6MDAgQ0VTVCIgIjIwMTItMTAtMjcgMDQ6MTU6MDAgQ0VTVCIKIyBbMzVdICIyMDEyLTEwLTI3IDA0OjMwOjAwIENFU1QiICIyMDEyLTEwLTI3IDA0OjQ1OjAwIENFU1QiCiMgWzM3XSAiMjAxMi0xMC0yNyAwNTowMDowMCBDRVNUIiAiMjAxMi0xMC0yNyAwNToxNTowMCBDRVNUIgoKIyBGT1IgMjAxMQpucm93KGNhbGxzLjIwMTFbaXMubmEoY2FsbHMuMjAxMSRuZWdfTVcpLCBdKQojIFsxXSAyMzUgLS0+IGNhLiAxLjMzJQpjYWxscy4yMDExW2lzLm5hKGNhbGxzLjIwMTEkbmVnX01XKSwgIkRhdGVUaW1lIl0KIyAoLi4pCgpgYGAKCgoqKkltcHV0ZSBNaXNzaW5nIFZhbHVlcyoqCgpGb3IgdGltZSBzZXJpZXMgYW5hbHlzaXMgb24gc3VjaCBhIGRhdGEgc2V0LCBpdCBpcyBub3QgYXBwcm9wcmlhdGUgdG8gbGVhdmUgdGhlbSBvdXQuIFRoZSBhbW91bnQgb2YgcHJvcG9ydGlvbiBpcyBxdWl0ZSBsZXNzLCBidXQgbmV2ZXJ0aGVsZXNzIGl0IGlzIGJldHRlciB0byBhcHByb3hpbWF0ZSB0aGUgbWlzc2luZyB2YWx1ZXMuIFRoZXJlIHdvdWxkIGJlIHRocmVlIGRpZmZlcmVudCBhcHByb2FjaGVzOgoKLSAqKlVzZSB0aGUgZGF0YSBvZiBvdGhlciB5ZWFyczoqKiBTaW5jZSB0aGVyZSBpcyBubyBpbnRlcnNlY3Rpb24gd2l0aCB0aGUgbWlzc2luZyBkYXRlcywgaXQgaXMgcG9zc2libGUgdG8gaW1wdXRlIHRoZSBtaXNzaW5nIHZhbHVlcyB3aXRoIHRoZSB2YWx1ZXMgb2YgdGhlIHllYXIgYmVmb3JlLiBPbmx5IHByb2JsZW0gaXMgdGhhdCBmb3IgMjAxMSAoYWxzbyBmb3IgZmVicnVhcnkgMjAxMikgbm8gZGF0YSBvZiAyMDEwIChmZWJydWFyeSAyMDExKSBpcyBhdmFpbGFibGUuIEFub3RoZXIgb3B0aW9uIHdvdWxkIGJlIHRvIHRha2UgYW4gYXZlcmFnZSBvZiB0aGUgKmZ1dHVyZSogZGF0YSBvZiB0aGUgeWVhcnMgYWZ0ZXIuCi0gKipVc2UgcmVzZXJ2ZSBuZWVkczoqKiBJdCBpcyBhbHNvIHBvc3NpYmxlIHRvIHVzZSB0aGUgNHNlYyByZXNlcnZlIG5lZWRzIHdoaWNoIGFyZSBhbHNvIHVzZWQgZm9yIHRoZSBtaW51dGVseSBhcHByb3hpbWF0aW9uIG9mIHRoZSBjYWxscy4KLSAqKlVzZSB0aGUgZGF0YSBvZiB0aGUgVFNPczoqKiBPbiB0aGUgZ2l2ZW4gZGF0ZSBvZiBtaXNzaW5nIHZhbHVlcywgc29tZSBUU08gc2hvdyBTUiBjYWxsIGRhdGEuIFNvIG9uZSBhcHByb2FjaCB3b3VsZCBiZSB0byBzdW0gdXAgdGhvc2Ugb2JzZXJ2YXRpb25zIGFuZCB1c2UgaXQgdG8gaW1wdXRlIHRoZSBtaXNzaW5nIHZhbHVlcyBmb3IgdGhlICpOZXR6cmVnZWx2ZXJidW5kKi4gCgoKV2Ugd2lsbCBnbyBmb3IgdGhlIGxhdHRlci4gVGFraW5nIHRoZSByZXNlcnZlIG5lZWRzIHdvdWxkIGNhdXNlIGEgYmlnIGRldmlhdGlvbiwgc2luY2UgdGhlIG5lZWRzIGFuZCBhY3R1YWwgY2FsbHMgZGlmZmVyIHJlbWFya2FibHkuIEF2ZXJhZ2luZyBmdXR1cmUgeWVhcnMgd291bGQgbGVhZCB0byBtaXNpbnRlcnByZXRhdGlvbnMuIFVzaW5nIFRTTyBkYXRhIHNlZW1zIG5hdHVyYWwgYmVjYXVzZSB0aGUgc3VtbWF0aW9uIG9mIHRoZSBmb3VyIHlpZWxkIGludG8gdGhlICpOZXR6cmVnZWx2ZXJidW5kKiB2YXJpYWJsZS4gSXQgaXMgYWxzbyBub3RpY2VkIHRoYXQgb25seSBvbmUgKG9ubHkgZm9yIDIwMTEgYXQgYDIwMTEtMTAtMzAgMjM6MDA6MDBgIGZvciBvbmUgaG91ciAzIFRTTyBsYWNrIHdpdGggZGF0YSkgVFNPIGhhcyBtaXNzaW5nIHZhbHVlcyBmb3IgdGhlIGRhdGVzIG9mIGludGVyZXN0IHdoaWNoIHNob3VsZCBrZWVwIHRoZSBlcnJvciBhbmQgYSBwb3NzaWJsZSBiaWFzIHNtYWxsLiBUaGlzIGxlYXZlcyB0aGUgcXVlc3Rpb24gaG93IHRvIGltcHV0ZSB0aGUgbWlzc2luZyB2YWx1ZSBvZiB0aGF0IFRTTy4gQSBzaW1wbGUgYXBwcm9hY2ggd291bGQgYmUgdG8gdGFrZSB0aGUgdmFsdWUgb2YgdGhlIGRheSBiZWZvcmUuIFRoaXMgaXMgcG9zc2libGUgYmVjYXVzZSB0aGUgZmlyc3Qgb2JzZXJ2YXRpb24gaGFzIG5vIG1pc3NpbmcgdmFsdWUoYDIwMTEtMDctMDFgKS4gVGhlIGNvZGUgc2FtcGxlIGJlbG93IGltcHV0ZXMgdGhlIGRhdGEgZm9yIHRoZSBhbHJlYWR5IGRvd25sb2FkZWQgYGRhdGEuZnJhbWVzYCBgY2FsbHMuMjAxM2AgYGNhbGxzLjIwMTJgIGFuZCBgY2FsbHMuMjAxMWAuCgpgYGB7cn0KIyBVc2UgdGhlIGNhbGwgZGF0YSBvZiB0aGUgNCBUU09zIHRvIGltcHV0ZSB0aGUgbWlzc2luZyB2YWx1ZXMgb2YgdGhlIGNhbGxzIChOZXR6cmVnZWx2ZXJidW5kKSBpbiAyMDEzLCAyMDEyIGFuZCAyMDExCgojIExvYWQgdGhlIFRTTyBkYXRhCiMgNTBIeiAoNCkKY2FsbHMuMjAxMy40ID0gZ2V0UmVzZXJ2ZUNhbGxzKCcwMS4wMS4yMDEzJywgJzMxLjEyLjIwMTMnLCAnNCcsICdTUkwnKQpjYWxscy4yMDEyLjQgPSBnZXRSZXNlcnZlQ2FsbHMoJzAxLjAxLjIwMTInLCAnMzEuMTIuMjAxMicsICc0JywgJ1NSTCcpCmNhbGxzLjIwMTEuNCA9IGdldFJlc2VydmVDYWxscygnMDEuMDcuMjAxMScsICczMS4xMi4yMDExJywgJzQnLCAnU1JMJykKIyBUZW5uZVQgKDIpCmNhbGxzLjIwMTMuMiA9IGdldFJlc2VydmVDYWxscygnMDEuMDEuMjAxMycsICczMS4xMi4yMDEzJywgJzInLCAnU1JMJykKY2FsbHMuMjAxMi4yID0gZ2V0UmVzZXJ2ZUNhbGxzKCcwMS4wMS4yMDEyJywgJzMxLjEyLjIwMTInLCAnMicsICdTUkwnKQpjYWxscy4yMDExLjIgPSBnZXRSZXNlcnZlQ2FsbHMoJzAxLjA3LjIwMTEnLCAnMzEuMTIuMjAxMScsICcyJywgJ1NSTCcpCiMgQW1wcmlvbiAoMykKY2FsbHMuMjAxMy4zID0gZ2V0UmVzZXJ2ZUNhbGxzKCcwMS4wMS4yMDEzJywgJzMxLjEyLjIwMTMnLCAnMycsICdTUkwnKQpjYWxscy4yMDEyLjMgPSBnZXRSZXNlcnZlQ2FsbHMoJzAxLjAxLjIwMTInLCAnMzEuMTIuMjAxMicsICczJywgJ1NSTCcpCmNhbGxzLjIwMTEuMyA9IGdldFJlc2VydmVDYWxscygnMDEuMDcuMjAxMScsICczMS4xMi4yMDExJywgJzMnLCAnU1JMJykKIyBUcmFuc25ldEJXICgxKQpjYWxscy4yMDEzLjEgPSBnZXRSZXNlcnZlQ2FsbHMoJzAxLjAxLjIwMTMnLCAnMzEuMTIuMjAxMycsICcxJywgJ1NSTCcpCmNhbGxzLjIwMTIuMSA9IGdldFJlc2VydmVDYWxscygnMDEuMDEuMjAxMicsICczMS4xMi4yMDEyJywgJzEnLCAnU1JMJykKY2FsbHMuMjAxMS4xID0gZ2V0UmVzZXJ2ZUNhbGxzKCcwMS4wNy4yMDExJywgJzMxLjEyLjIwMTEnLCAnMScsICdTUkwnKQoKdHNvLmxpc3QuMjAxMyA8LSBsaXN0KGNhbGxzLjIwMTMuMSxjYWxscy4yMDEzLjIsY2FsbHMuMjAxMy4zLGNhbGxzLjIwMTMuNCkKdHNvLmxpc3QuMjAxMiA8LSBsaXN0KGNhbGxzLjIwMTIuMSxjYWxscy4yMDEyLjIsY2FsbHMuMjAxMi4zLGNhbGxzLjIwMTIuNCkKdHNvLmxpc3QuMjAxMSA8LSBsaXN0KGNhbGxzLjIwMTEuMSxjYWxscy4yMDExLjIsY2FsbHMuMjAxMS4zLGNhbGxzLjIwMTEuNCkKCiMKIyBJbXB1dGUgbWlzc2luZyBjYWxscyB3aXRoIFRTTyBkYXRhCiMKaW1wdXRlZC5jYWxscy4yMDEzIDwtIGltcHV0ZU1pc3NpbmdDYWxsc1dpdGhUU08oY2FsbHMuMjAxMywgdHNvLmxpc3QuMjAxMykKaW1wdXRlZC5jYWxscy4yMDEyIDwtIGltcHV0ZU1pc3NpbmdDYWxsc1dpdGhUU08oY2FsbHMuMjAxMiwgdHNvLmxpc3QuMjAxMikKaW1wdXRlZC5jYWxscy4yMDExIDwtIGltcHV0ZU1pc3NpbmdDYWxsc1dpdGhUU08oY2FsbHMuMjAxMSwgdHNvLmxpc3QuMjAxMSkKCiMgU2FuaXR5IGNoZWNrIHRoYXQgbm8gTkEgYXJlIGxlZnQKaW1wdXRlZC5jYWxscy4yMDEzW2lzLm5hKGltcHV0ZWQuY2FsbHMuMjAxMyRuZWdfTVcpLCBdCmltcHV0ZWQuY2FsbHMuMjAxMltpcy5uYShpbXB1dGVkLmNhbGxzLjIwMTIkbmVnX01XKSwgXQppbXB1dGVkLmNhbGxzLjIwMTFbaXMubmEoaW1wdXRlZC5jYWxscy4yMDExJG5lZ19NVyksIF0KCmBgYAo+IEZ1cnRoZXIgcmVmZXJlbmNlcyBpbiB0aGUgYW5hbHlzaXMgdG8gYGNhbGxzLjIwMTNgIGBjYWxscy4yMDEyYCBhbmQgYGNhbGxzLjIwMTFgIGNvbnRhaW4gdGhlIGltcHV0ZWQgZGF0YSBzZXRzLgoKCgoKIyMjIyMgQXBwcm94aW1hdGlvbiBvZiBtaW51dGVseSBjYWxscwoKU2luY2Ugd2UgbmVlZCBhIGhpZ2hlciByZXNvbHV0aW9uIGZvciB0aGUgb3BlcmF0aXZlIHJlc2VydmUgY2FsbHMgKG9uZSBtaW51dGUgaW5zdGVhZCBvZiAxNSBtaW51dGVzKSwgdGhlIG5leHQgc3RlcCBpcyBub3cgdG8gYXBwcm94aW1hdGUgb25lIG1pbnV0ZSBvcGVyYXRpdmUgcmVzZXJ2ZSBjYWxscyBmcm9tIHRoZSBvcGVyYXRpdmUgcmVzZXJ2ZSBuZWVkcyBkYXRhLiAKClRoZSBhcHByb3hpbWF0aW9uIGZvbGxvd3Mgc2V2ZXJhbCBzdGVwczoKCjEuIFRoZSA0c2VjIG9wZXJhdGl2ZSByZXNlcnZlIG5lZWRzIGFyZSBhdmVyYWdlZCBvbiBhbiBvbmUgbWludXRlIGJhc2UuIFRob3NlIDE1IG9ic2VydmF0aW9ucyBidWlsZCB0aGUgYmFzZSB2YWx1ZSB3aGljaCBpcyBnb2luZyB0byBiZSBjb3JyZWN0ZWQgYnkgdGhlIGRpZmZlcmVuY2Ugb2YgaXRzIDE1bWluIGF2ZXJhZ2UgYW5kIHRoZSByZXNlcnZlIGNhbGwgdmFsdWUuIAoKMi4gVGhlIDE1bWluIHJlc2VydmUgbmVlZCBhdmVyYWdlcyBhcmUgY29tcHV0ZWQgZm9yIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSBuZWVkcyBhbmQgY29tcGFyZWQgdG8gdGhlIHBvc2l0aXZlIGFuZCBuZWdhdGl2ZSByZXNlcnZlIGNhbGxzIHJlc3BlY3RpdmVseS4KCjMuIFRoZSBkaWZmZXJlbmNlIHZhbHVlIG9mIHRoZSBwb3NpdGl2ZSAobmVnYXRpdmUpIGF2ZXJhZ2UgZ2V0cyBldmVubHkgZGlzdHJpYnV0ZWQgYW1vbmcgdGhlIHBvc2l0aXZlIChuZWdhdGl2ZSkgbWludXRlbHkgcmVzZXJ2ZSBuZWVkcyB3aXRoaW4gdGhhdCAxNSBtaW51dGVzLiAKCjQuIFRoaXMgY29ycmVjdGVkIGF2ZXJhZ2UgZGlmZmVyZW5jZSAoZnJhY3Rpb25lZCBieSB0aGUgbnVtYmVyIG9mIHBvc2l0aXZlIChuZWdhdGl2ZSkgcmVzZXJ2ZSBuZWVkcykgaXMgYWRkZWQgdXAgdG8gdGhlIG9uZSBtaW51dGUgcmVzZXJ2ZSBuZWVkIHZhbHVlLgoKQWZ0ZXIgdGhpcyBwcm9jZWR1cmUgdGhlIDE1IG1pdW50ZSBhdmVyYWdlcyBvZiB0aGUgY29ycmVjdGVkIG9uZSBtaW51dGUgcmVzZXJ2ZSBuZWVkIHNob3VsZCBlcXVhbCB0aGUgcmV0cmlldmVkIHJlc2VydmUgY2FsbHMgKGZvciBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgY2FsbHMgcmVzcGVjdGl2ZWx5KS4gVGhlIGZpZ3VyZSBiZWxvdyBzaG93cyB2aXN1YWxseSB0aGUgYXBwcm94aW1hdGlvbi4gVGhlIGxpZ2h0IGdyZXkgc29saWQgbGluZSByZXByZXNlbnRzIHRoZSA0c2VjIHJlc2VydmUgbmVlZHMgb24gdGhlIGAyMDE2LTAxLTAxIDAwOjE1IC0gMDA6MzBgLiBUaGUgc29saWQgYmx1ZSBzdGVwIGxpbmUgaXMgdGhlIDFtaW4gYXZlcmFnZSBhbmQgdGhlIHNvbGlkIGdyZWVuIChyZWQpIGxpbmUgaXMgdGhlIDE1bWluIGF2ZXJhZ2Ugb2YgcG9zaXRpdmUgKG5lZ2F0aXZlKSByZXNlcnZlIG5lZWRzLiBPbiB0aGUgb3RoZXIgaGFuZCwgeW91IGNhbiBzZWUgdGhlIGRhc2hlZCBncmVlbiAocmVkKSBsaW5lIHdoaWNoIHJlcHJlc2VudHMgdGhlIDE1bWluIHJlc2VydmUgY2FsbHMuIFNpbmNlIHRoZSB0aW1lIHBlcmlvZCBpcyBvbmx5IDE1IG1pbnV0ZXMgd2Ugc2VlIGEgc3RyYWlnaHQgbGluZS4gVGhlIGFwcHJveGltYXRlZCAxbWluIGNhbGwgaXMgdGhlIGRhc2hlZCBibHVlIHN0ZXAgbGluZS4gWW91IGNhbiBub3RpY2UgdGhlIGRvd253YXJkIGNvcnJlY3Rpb24gZm9yIHRoZSBuZWdhdGl2ZSByZXNlcnZlcywgZHVlIHRvIHRoZSBuZWdhdGl2ZSBkZXZpYXRpb24gb2YgdGhlIDE1bWluIGF2ZXJhZ2UgYW5kIHRoZSByZXNlcnZlIGNhbGwgKHNvbGlkIHZzLiBkYXNoZWQgcmVkIGxpbmUpLiBWaWNlIHZlcnNhLCB0aGUgcG9zaXRpdmUgbmVlZHMgYXJlIGNvcnJlY3RlZCB1cHdhcmRzLiBUaGUgY29ycmVjdGlvbnMgYXJlIGFsbCBvZiB0aGUgc2FtZSBhbW91bnQgZm9yIGVhY2ggbWludXRlIChkZXBlbmRpbmcgb24gcG9zaXRpdmUgb3IgbmVnYXRpdmUgbnVtYmVyIG9mIG1pbnV0ZXMpIGFuZCBhcmUgc2xpZ2h0bHkgaGlnaGVyIHRoYW4gdGhlIGRlbHRhIGJldHdlZW4gdGhlIDE1bWluIGF2ZXJhZ2Ugb2YgdGhlIHJlc2VydmUgbmVlZHMgYW5kIHRoZSBjYWxscywgYmVjYXVzZSBsZXNzIHBvc2l0aXZlIChuZWdhdGl2ZSkgbWludXRlcyBoYXZlIHRvIGNvbXBlbnNhdGUgdGhlIHdob2xlIDE1IG1pbnV0ZSB2YWx1ZS4KCiFbQXBwcm94aW1hdGlvbiBvZiAxbWluIGNhbGxzICgyMDE2LTAxLTAxIDAwOjE1IC0gMDA6MzApXShhcHgubm9ybWFsLnBuZyl7IHdpZHRoPTEyMCUgfQoKCioqU3BlY2lhbCBjYXNlcyoqCgoKU28gZmFyIHlvdSBoYXZlIHNlZW4gYSBub3JtYWwgY2FzZS4gQnV0IHRoZSBkZXZpYXRpb25zIGJldHdlZW4gcmVzZXJ2ZSBuZWVkcyBhbmQgY2FsbHMgY2FuIGJlIGRpdmVyZ2VudCBzdWNoIHRoYXQgdGhlIDE1bWluIGF2ZXJhZ2UgZm9yIHRoZSBuZWVkcyBzaG93cyBubyB2YWx1ZSB3aGVyZSBhcyByZXNlcnZlIGNhbGxzIGhhZCBiZWVuIGFjdGl2YXRlZC4gVGhpcyBzcGVjaWFsIGNhc2UgaXMgY2FsbGVkICoqaG9tb2dlbml0eSoqLiBUaGVyZSBjYW4gYmUgdHdvIHR5cGVzIG9mIGhvbW9nZW5pdHk6CgotICpOZWdhdGl2ZSBob21vZ2VuaXR5KiBvY2N1cnMgaWYgYWxsIDFtaW4gYXZlcmFnZWQgbmVlZHMgd2l0aGluIHRoZSAxNW1pbiBwZXJpb2QgYXJlIG5lZ2F0aXZlIChpLmUuIDE1bWluIGF2ZXJhZ2UgaXMgemVybyksIGJ1dCB0aGUgMTUgbWludXRlIHJlc2VydmUgY2FsbCBzaG93IGRlbWFuZCBmb3IgcG9zaXRpdmUgcmVzZXJ2ZSBwb3dlci4gCgotICpQb3NpdGl2ZSBob21vZ2VuaXR5KiBvY2N1cnMgaWYgYWxsICAxbWluIGF2ZXJhZ2VkIG5lZWRzIHdpdGhpbiB0aGUgMTVtaW4gcGVyaW9kIGFyZSBwb3NpdGl2ZSAoaS5lLiAxNW1pbiBhdmVyYWdlIGlzIHplcm8pLCBidXQgdGhlIDE1IG1pbnV0ZSByZXNlcnZlIGNhbGwgc2hvdyBkZW1hbmQgZm9yIG5lZ2F0aXZlIHJlc2VydmUgcG93ZXIuIAoKVGhvc2UgdHdvIGNhc2VzIGFyZSBpbGx1c3RyYXRlZCBpbiB0aGUgZm9sbG93aW5nIHR3byBncmFwaHMuIFRoZSBmaXJzdCBncmFwaCBzaG93cyBuZWdhdGl2ZSBob21vZ2VuaXR5LiBIZXJlYnksIGFsbCBibHVlIHNvbGlkIGxpbmVzIGFyZSBpbiB0aGUgbmVnYXRpdmUgcG93ZXIsIGJ1dCB0aGUgZGFzaGVkIGdyZWVuIGxpZ2h0IGlzIG5vdCBhbGlnbmVkIHdpdGggdGhlIHNvbGlkIGdyZWVuIGxpbmUgb24gdGhlIHZhbHVlIG9mIDAgTVcuIFRoaXMgbWVhbnMgdGhlcmUgd2FzIGRlbWFuZCBmb3IgcG9zaXRpdmUgcmVzZXJ2ZSBwb3dlci4gVGhlIHNlY29uZCBwbG90IGRlYWxzIHdpdGggdGhlIG9wcG9zaXRlLCBuZWdhdGl2ZSBob21vZ2VuaXR5IGNhc2UuCgohW0FwcHJveGltYXRpb24gb2YgMW1pbiBjYWxscyB3aXRoIHNwZWNpYWwgY2FzZSBvZiBuZWdhdGl2ZSBob21vZ2VuaXR5ICgyMDE2LTAxLTAxIDAwOjAwIC0gMDA6MTUpXShhcHgubmVnLnNwZWNpYWwucG5nKXsgd2lkdGg9MTIwJSB9CgoKIVtBcHByb3hpbWF0aW9uIG9mIDFtaW4gY2FsbHMgd2l0aCBzcGVjaWFsIGNhc2Ugb2YgcG9zaXRpdmUgaG9tb2dlbml0eSAoMjAxNi0wMS0wMSAwMDozMCAtIDAwOjQ1KV0oYXB4LnBvcy5zcGVjaWFsLnBuZyl7IHdpZHRoPTEyMCUgfQoKVG8gZW5jb3VudGVyIHRob3NlIHNwZWNpYWwgY2FzZXMuIFdlIGhhdmUgdG8gYWRkIGEgbWludXRlIG9mIHBvc2l0aXZlIChpbiB0aGUgbmVnYXRpdmUgaG9tb2dlbnRpeSBjYXNlKSBvciBuZWdhdGl2ZSAoaW4gdGhlIHBvc2l0aXZlIGhvbW9nZW5pdHkgY2FzZSkgIHJlc2VydmUgcG93ZXIgd2hpY2ggY29tcGVuc2F0ZXMgdGhlIHdob2xlIDE1IG1pbnV0ZSByZXNlcnZlIGNhbGwgcG93ZXIuIFRoZSBjaG9zZW4gbWludXRlIHdpbGwgYmUgdGhlIHNtYWxsZXN0IGFic29sdXRlIHZhbHVlIG9mIHRoZSBwb3NpdGl2ZSAobmVnYXRpdmUpIHBvd2Vycy4gSGVuY2UsIHRoZSAxNSBtaW51dGUgYXZlcmFnZXMgaGF2ZSB0byBiZSBuZXdseSBjb21wdXRlZC4gQW5kIHRoZW4gdGhlIG5ldyBkaWZmZXJlbmNlIHlpZWxkcyBpbiB0aGUgbmV3IGNvcnJlY3Rpb24gdmFsdWUuIAoKVGhlIG5leHQgcGxvdCBzaG93cyB0aGUgcmVzdWx0IG9mIHRoZSBjb3JyZWN0aW9uIG9mIHRoZSBuZWdhdGl2ZSBob21vZ2VuaXR5IGNhc2UgZXhhbXBsZS4gWW91IGNhbiBzZWUgdGhhdCB0aGUgbWludXRlIG9mIHRoZSAxbWluIGF2ZXJhZ2VkIHJlc2VydmUgbmVlZCB3aXRoIHRoZSBsb3dlc3QgbmVnYXRpdmUgcG93ZXIgdmFsdWUgaXMgc2VsZWN0ZWQgYXMgdGhlIHBvc2l0aXZlIHJlc2VydmUgcG93ZXIgbWludXRlIHRvIGNvbXBlbnNhdGUgdGhlIHdob2xlIDE1IG1pbnV0ZXMuIFNpbWlsYXJseSwgdGhlIDE1IG1pbnV0ZSBhdmVyYWdlIGZvciB0aGUgbmVnYXRpdmUgKm1pbnV0ZXMqIGlzIHVwZGF0ZWQgYW5kIHRoZSBuZXcgZGVsdGEgaXMgYWRkZWQgdXAgZm9yIHRoZSBjb3JyZWN0aW9uIG9mIHRob3NlIG1pbnV0ZXMuIE5vdyB0aGUgZGFzaGVkIGFuZCBzb2xpZCBsaW5lcyBhcmUgYWxpZ25lZC4KCiFbRmluYWwgcmVzdWx0IG9mIHRoZSBhcHByb3hpbWF0aW9uIG9mIDFtaW4gY2FsbHMgd2l0aCBzcGVjaWFsIGNhc2Ugb2YgbmVnYXRpdmUgaG9tb2dlbml0eSAoMjAxNi0wMS0wMSAwMDowMCAtIDAwOjE1KV0oYXB4Lm5lZy5zcGVjaWFsLjIucG5nKXsgd2lkdGg9MTIwJSB9CgoKCgpOb3cgaXQgY291bGQgaGFwcGVuIHRoYXQgd2l0aCB0aGUgbmV3IGNvcnJlY3Rpb24gYW5vdGhlciAob3IgbW9yZSkgYXBwcm94aW1hdGVkIDEgbWludXRlIGNhbGwocykgc3dpdGNoZXMgaXRzIHNpZ24uIFRoaXMgY2FzZSBpcyBjYWxsZWQgKip6ZXJvIGNyb3NzaW5nKiouIEFsbCBpbiBhbGwgdGhpcyBmb3JjZXMgdG8gYW4gaXRlcmF0aXZlIGFwcHJveGltYXRpb24gcHJvY2Vzcy4gVGhlcmVieSwgdGhlIHZhbHVlcyBnZXQgY29ycmVjdGVkIGFzIGxvbmcgYXMgdGhlIG5ldyAxNW1pbiBhdmVyYWdlIGVxdWFscyB0aGUgcmVzZXJ2ZSBjYWxsLiBUaGUgZ3JhcGhpYyBiZWxvdyBpbGx1c3RyYXRlcyBzdWNoIGEgY2FzZSBvbiB0aGUgYDIwMTYtMDEtMDEgMTI6MTVgLiBUaGUgYXBwcm94aW1hdGlvbiwgYXMgaXQgd291bGQgb2NjdXIgaW4gdGhlIG5vcm1hbCBjYXNlLCB5aWVsZHMgdG8gY2hhbmdlIHNpZ25zIGZvciBmb3VyIG1pbnV0ZXMgd2hpY2ggc2hvd2VkIG5lZ2F0aXZlIHJlc2VydmUgcG93ZXIgbmVlZHMgYmVmb3JlLiBBdCB0aGlzIHN0YXRlIHRoZSBwb3NpdGl2ZSAxNSBtaW51dGUgYXZlcmFnZSAoYWNjb3VudGluZyB0aGUgZm91ciBtaW51dGUgdmFsdWVzKSB3b3VsZCBub3QgbWF0Y2ggd2l0aCB0aGUgcmVzZXJ2ZSBjYWxsIHZhbHVlLiBIZW5jZSwgYW5vdGhlciBpdGVyYXRpb24gKGNvcnJlY3Rpb24gcHJvY2VzcykgaXMgbmVlZGVkIHRvIGVxdWFsaXplIGJvdGggdmFsdWVzLgoKIVtGaXJzdCBpdGVyYXRpb24gb2YgdGhlIGFwcHJveGltYXRpb24gb2YgMW1pbiBjYWxscyB3aXRoIHplcm8gY3Jvc3NpbmcgdmFsdWVzICgyMDE2LTAxLTAxIDEyOjE1IC0gMTI6MzApXShhcHguemVyby5jcm9zcy4xLnBuZyl7IHdpZHRoPTEyMCUgfQoKSW4gdGhlIHBsb3QgYmVsb3cgeW91IGNhbiBzZWUgdGhlIHJlc3VsdCBhZnRlciBhbm90aGVyIGNvcnJlY3Rpb24uIE5vdywgdGhlIDE1IG1pbnV0ZSBhdmVyYWdlIG9mIHRoZSByZXNlcnZlIG5lZWRzIGVxdWFscyB0aGUgcmVzZXJ2ZSBjYWxscy4gVGhlIHZhbHVlcyBvZiB0aGUgZm91ciBwb3NpdGl2ZSAqbWludXRlcyogd2VyZSBsb3dlcmVkLCB3aGVyZSBhcyB0aGUgbmVnYXRpdmUgKm1pbnV0ZXMqIHJvc2Ugc2luY2UgZmV3ZXIgdmFsdWVzIGhhdmUgdG8gY29tcGVuc2F0ZSB0aGUgcG9zaXRpdmUgZGVsdGEgYmV0d2VlbiB0aGUgcmVzZXJ2ZSBjYWxscyBhbmQgbmVlZHMuCgohW0ZpbmFsIGl0ZXJhdGlvbiBvZiB0aGUgYXBwcm94aW1hdGlvbiBvZiAxbWluIGNhbGxzIHdpdGggemVybyBjcm9zc2luZyB2YWx1ZXMgKDIwMTYtMDEtMDEgMTI6MTUgLSAxMjozMCldKGFweC56ZXJvLmNyb3NzLjIucG5nKXsgd2lkdGg9MTIwJSB9CgoKVGhlIGBybWFya2V0Y3Jhd2xSYCBwYWNrYWdlIGluY2x1ZGVzIGEgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBhcHByb3hpbWF0aW9uLiBUaGUgY29kZSBzbmlwcGV0IGJlbG93IHNob3dzIGFuIGV4YW1wbGUgb24gaG93IHRvIGdldCB0aGUgYXBwcm94aW1hdGlvbnMgZm9yIDIwMTYuIElmIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiB0aGUgbWFyZ2luYWwgd29yayBwcmljZXMgKGFzIHdlIGFyZSksIHlvdSBjYW4gc2tpcCB0aGlzIGZ1bmN0aW9uLCBzaW5jZSB0aGUgbWV0aG9kIGZvciBjb21wdXRpbmcgbWFyZ2luYWwgd29yayBwcmljZXMgYWxyZWFkeSBjb250YWlucyB0aGlzIGNvcnJlY3Rpb24gcHJvY2VkdXJlLiAKCmBgYHtyfQojIENhbGN1bGF0ZSB0aGUgYXBwcm94aW1hdGVkIDFtaW4gY2FsbHMgZnJvbSB0aGUgNHNlYyBvcGVyYXRpbmcgcmVzZXJ2ZSBuZWVkcyBkYXRhCmFwcHJveC5jYWxscy4yMDE2ID0gZ2V0T25lTWludXRlQ2FsbHMobmVlZHMuMjAxNiwgY2FsbHMuMjAxNikKCmBgYAoKCgojIyMjIyBDYWxjdWxhdGlvbiBvZiBtYXJnaW5hbCB3b3JrIHByaWNlcyBhbmQgY2FsbCBwcm9iYWJpbGl0aWVzCgpJbiB0aGUgY2hhcHRlcnMgYmVmb3JlIHdlIGRpdmVkIGludG8gdGhlIGRhdGEgYW5kIHByZS1wcm9jZXNzZWQgaXQuIEl0IHdhcyBhbHNvIHNob3duIGhvdyB0aGUgYXBwcm94aW1hdGlvbiBvZiB0aGUgb25lIG1pbnV0ZSBjYWxscyBpcyBjYWxjdWxhdGVkLiBOb3cgaXQgaXMgdGltZSB0byB1c2UgdGhlIHByZS1wcm9jZXNzZWQgZGF0YSB0byBjYWxjdWxhdGUgdGhlIG1hcmdpbmFsIHdvcmsgcHJpY2VzIGZvciBldmVyeSBhcHByb3hpbWF0ZWQgbWludXRlLiBUaGUgb3V0cHV0IG9mIHRoZSBhcHByb3hpbWF0aW9uIGZ1bmN0aW9uIGBnZXRPbmVNaW51dGVDYWxscygpYCBpcyBub3QgbmVlZGVkLCBzaW5jZSB0aGUgYGdldE1hcmdpbmFsV29ya1ByaWNlcygpYCBhbHNvIGNvbXB1dGVzIHRoZSBhcHByb3hpbWF0aW9uLiAKClRoZSBjYWxjdWxhdGlvbiBpcyBzdHJhaWdodCBmb3J3YXJkLiBGb3IgZXZlcnkgbWludXRlIChvYnNlcnZhdGlvbikgdGhlIGFwcHJveGltYXRlZCByZXNlcnZlIHBvd2VyIChwb3NpdGl2ZSBvciBuZWdhYXRpdmUpLCB0aGUgcmVsYXRlZCB0aW1lIChhbmQgaXRzIHRhcmlmIGluZGljYXRpb24pIGlzIG1hdGNoZWQgdXAgd2l0aCB0aGUgYXVjdGlvbnMgZGF0YS4gVGhlIG1hdGNoIHVwIG9yZGVycyB0aGUgYXVjdGlvbnMgZnJvbSBsb3dlc3QgdG8gaGlnaGVzdCB3b3JrIHByaWNlIGJpZC4gSXRzIG9mZmVyZWQgcG93ZXIgaXMgY3VtdWxhdGVkLiBUaGUgYmlkIChvZmZlcikgd2hlcmUgaXRzIGN1bXVsYXRlZCB2YWx1ZSBlcXVhbHMgb3IgZXhjZWVkcyB0aGUgYXBwcm94aW1hdGVkIHJlc2VydmUgcG93ZXIgZGVmaW5lcyB0aGUgbWFyZ2luYWwgd29yayBwcmljZS4gT2ZmZXJzIGVxdWFsIG9yIGJlbG93IHRoaXMgcHJpY2UgaGFkIGJlZW4gY2FsbGVkIHRvIGRlbGl2ZXIgdGhlIHJlc2VydmUgcG93ZXIuCgpUbyBwcm9jZWVkIHdpdGggdGhlIGFuYWx5c2lzLCB3ZSB3aWxsIG5vdyB1c2UgdGhlIGBnZXRNYXJnaW5hbFdvcmtQcmljZXMoKWAgb2YgdGhlIFIgcGFja2FnZSB0byBjYWxjdWxhdGUgdGhlIG1hcmdpbmFsIHdvcmsgcHJpY2VzIGZvciAyMDExIHRpbGwgMjAxNiBvbiBhIG1pbnV0ZWx5IHJlc29sdXRpb24uCmBgYHIKIyBDYWxjdWxhdGUgdGhlIG1hcmdpbmFsIHdvcmsgcHJpY2VzIGZyb20gdGhlIHByZXByb2Nlc3NlZCByYXcgaW5wdXQgZGF0YSAoc2VlIHNlY3Rpb25zIGFib3ZlKSBmb3IgdGhlIHllYXJzIDIwMTEgLSAyMDE2Cm13cC4yMDE2ID0gZ2V0TWFyZ2luYWxXb3JrUHJpY2VzKG5lZWRzLjIwMTYsIGNhbGxzLjIwMTYsIGF1Y3Rpb25zLjIwMTYsIG51bUNvcmVzID0gMikKbXdwLjIwMTUgPSBnZXRNYXJnaW5hbFdvcmtQcmljZXMobmVlZHMuMjAxNSwgY2FsbHMuMjAxNSwgYXVjdGlvbnMuMjAxNSwgbnVtQ29yZXMgPSAyKQptd3AuMjAxNCA9IGdldE1hcmdpbmFsV29ya1ByaWNlcyhuZWVkcy4yMDE0LCBjYWxscy4yMDE0LCBhdWN0aW9ucy4yMDE0LCBudW1Db3JlcyA9IDIpCm13cC4yMDEzID0gZ2V0TWFyZ2luYWxXb3JrUHJpY2VzKG5lZWRzLjIwMTMsIGNhbGxzLjIwMTMsIGF1Y3Rpb25zLjIwMTMsIG51bUNvcmVzID0gMikKbXdwLjIwMTIgPSBnZXRNYXJnaW5hbFdvcmtQcmljZXMobmVlZHMuMjAxMiwgY2FsbHMuMjAxMiwgYXVjdGlvbnMuMjAxMiwgbnVtQ29yZXMgPSAyKQptd3AuMjAxMSA9IGdldE1hcmdpbmFsV29ya1ByaWNlcyhuZWVkcy4yMDExLCBjYWxscy4yMDExLCBhdWN0aW9ucy4yMDExLCBudW1Db3JlcyA9IDIpCgojIENvbWJpbmUgYWxsIHllYXJzIGludG8gb25lIGRhdGEgc2V0Cm13cC4yMDExLjIwMTYgPSByYmluZChtd3AuMjAxMSwgbXdwLjIwMTEyLCBtd3AuMjAxMywgbXdwLjIwMTQswqBtd3AuMjAxNSwgbXdwLjIwMTYpCmBgYAoKTm93LCB3ZSBoYXZlIGNvbWJpbmVkIGFsbCB0aGUgeWVhcnMgb2YgZGF0YSB0byBhIHNpbmdsZSBkYXRhLmZyYW1lLiBMZXQgdXMgaGF2ZSBsb29rIGF0IHRoZSBmaXJzdCBvYnNlcnZhdGlvbnMgYW5kIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEuCgpgYGB7cn0KaGVhZChtd3AuMjAxMS4yMDE2KQpgYGAKYGBge3J9CnN0cihtd3AuMjAxMS4yMDE2KQpgYGAKCgoKIyMjIyBFeHBsb3JlIERhdGEKCiMjIyMjIEF1Y3Rpb25zIERhdGEKCkZpcnN0bHksIHdlIHdpbGwgaW52ZXN0aWdhdGUgdGhlIGF1Y3Rpb24gcmVzdWx0cyBmb3IgdGhlIFNSIGluIDIwMTYuIFNpbmNlIHdlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSB3b3JrIHByaWNlcywgd2UgYXJlIGdvaW5nIHRvIHBsb3QgdGhlIGF2ZXJhZ2Ugd29yayBwcmljZSBiaWQgZm9yIGVhY2ggd2VlayBzZXBlcmF0ZWQgYnkgbWFpbiBhbmQgc3ViIHBlcmlvZCBhcyB3ZWxsIGFzIG5lZ2F0aXZlIGFuZCBwb3NpdGl2ZSBTUi4KCmBgYHtyfQojIGZpbHRlciBieSBkaXJlY3Rpb24KIyBhbmQgZ2V0IHRoZSBhdmVyYWdlIG9mIHRoZSB3b3JrIHByaWNlIG9mZmVycwphdWN0aW9ucy4yMDE2ICU+JQogIGdyb3VwX2J5XyguZG90cyA9IGMoImRhdGVfZnJvbSIsICJEaXJlY3Rpb24iKSkgJT4lCiAgc3VtbWFyaXNlKGF2ZyA9IG1lYW4od29ya19wcmljZSkpICU+JQogIGdncGxvdChhZXMoeCA9IGRhdGVfZnJvbSwgeSA9IGF2ZywgY29sb3VyID0gRGlyZWN0aW9uKSkgKyAKICAgICAgICBnZW9tX2xpbmUoKQoKYGBgCgoKCiMjIyMjIE1hcmdpbmFsIFdvcmsgUHJpY2VzCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkYXRhLnBvcywgYWVzKHg9bWFyZ2luYWxfd29ya19wcmljZSkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeSA9ICguLmNvdW50Li4pL3N1bSguLmNvdW50Li4pKSwgYnJlYWtzID0gc2VxKDAsIDEwMCwgMSksCiAgICAgICAgICAgICAgICAgY29sPSJncmVlbiIsCiAgICAgICAgICAgICAgICAgZmlsbD0iZ3JlZW4iLAogICAgICAgICAgICAgICAgIGFscGhhID0gLjUpICsKICBsYWJzKHRpdGxlPSJIaXN0b2dyYW0gZm9yIE1hcmdpbmFsIFdvcmsgUHJpY2VzIHdpdGggcG9zaXRpdmUgUmVzZXJ2ZSBQb3dlciBpbiAyMDE2IikgKwogIGxhYnMoeD0iTWFyZ2luYWwgV29yayBQcmljZSIsIHk9IkNvdW50IikKCmBgYAoKCgoKCgo=